Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
En DbContext-instans spårar automatiskt entiteter som returneras från databasen. Ändringar som görs i dessa entiteter identifieras sedan när SaveChanges anropas och databasen uppdateras efter behov. Mer information finns i Grundläggande sparande och relaterade data .
Ibland efterfrågas dock entiteter med hjälp av en kontextinstans och sparas sedan med en annan instans. Detta sker ofta i "frånkopplade" scenarier, till exempel ett webbprogram där entiteterna efterfrågas, skickas till klienten, ändras, skickas tillbaka till servern i en begäran och sedan sparas. I det här fallet måste den andra kontextinstansen veta om entiteterna är nya (ska infogas) eller befintliga (bör uppdateras).
Tips/Råd
Du kan visa den här artikelns exempel på GitHub.
Tips/Råd
EF Core kan bara spåra en instans av en entitet med ett visst primärnyckelvärde. Det bästa sättet att undvika att det här är ett problem är att använda en kortvarig kontext för varje arbetsenhet så att kontexten börjar tom, har entiteter kopplade till den, sparar dessa entiteter och sedan tas kontexten bort och ignoreras.
Identifiera nya entiteter
Klienten identifierar nya entiteter
Det enklaste fallet att hantera är när klienten informerar servern om entiteten är ny eller befintlig. Till exempel skiljer sig ofta begäran om att infoga en ny entitet från begäran om att uppdatera en befintlig entitet.
Resten av det här avsnittet beskriver de fall där det är nödvändigt att på något annat sätt avgöra om du vill infoga eller uppdatera.
Med automatiskt genererade nycklar
Värdet för en automatiskt genererad nyckel kan ofta användas för att avgöra om en entitet behöver infogas eller uppdateras. Om nyckeln inte har angetts (dvs. att den fortfarande har CLR-standardvärdet null, noll osv.) måste entiteten vara ny och måste infogas. Om nyckelvärdet å andra sidan har angetts måste det redan ha sparats tidigare och behöver nu uppdateras. Med andra ord, om nyckeln har ett värde, efterfrågades entiteten, skickades till klienten och har nu kommit tillbaka för att uppdateras.
Det är enkelt att kontrollera en oavsatt nyckel när entitetstypen är känd.
public static bool IsItNew(Blog blog)
=> blog.BlogId == 0;
Ef har dock också ett inbyggt sätt att göra detta för alla entitetstyper och nyckeltyper:
public static bool IsItNew(DbContext context, object entity)
=> !context.Entry(entity).IsKeySet;
Tips/Råd
Nycklar anges så snart entiteter spåras av kontexten, även om entiteten är i tillståndet Lägg till. Detta hjälper dig när du går igenom en graf med entiteter och bestämmer vad du ska göra med var och en, till exempel när du använder TrackGraph-API:et. Nyckelvärdet bör endast användas på det sätt som visas här innan något anrop görs för att spåra entiteten.
Med andra nycklar
En annan mekanism krävs för att identifiera nya entiteter när nyckelvärden inte genereras automatiskt. Det finns två allmänna metoder för detta:
- Fråga efter entiteten
- Skicka en flagga från klienten
Om du vill fråga efter entiteten använder du bara metoden Sök:
public static async Task<bool> IsItNew(BloggingContext context, Blog blog)
=> (await context.Blogs.FindAsync(blog.BlogId)) == null;
Det ligger utanför rambeskrivningen för det här dokumentet att presentera den kompletta koden för att skicka en flagga från en klient. I en webbapp innebär det vanligtvis att göra olika begäranden för olika åtgärder, eller att skicka ett tillstånd i begäran och sedan extrahera den i kontrollanten.
Spara enskilda entiteter
Om det är känt om en infogning eller uppdatering behövs eller inte kan antingen Lägg till eller Uppdatera användas på rätt sätt:
public static async Task Insert(DbContext context, object entity)
{
context.Add(entity);
await context.SaveChangesAsync();
}
public static async Task Update(DbContext context, object entity)
{
context.Update(entity);
await context.SaveChangesAsync();
}
Men om entiteten använder automatiskt genererade nyckelvärden kan metoden Uppdatera användas för båda fallen:
public static async Task InsertOrUpdate(DbContext context, object entity)
{
context.Update(entity);
await context.SaveChangesAsync();
}
Metoden Update markerar normalt entiteten för uppdatering, inte infoga. Men om entiteten har en automatiskt genererad nyckel och inget nyckelvärde har angetts markeras entiteten i stället automatiskt för insert.
Om entiteten inte använder automatiskt genererade nycklar måste programmet bestämma om entiteten ska infogas eller uppdateras: Till exempel:
public static async Task InsertOrUpdate(BloggingContext context, Blog blog)
{
var existingBlog = await context.Blogs.FindAsync(blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
}
await context.SaveChangesAsync();
}
Stegen här är:
- Om Find returnerar null betyder det att databasen inte redan innehåller bloggen med det här ID:t, så vi anropar Add för att markera den för infogning.
- Om Find returnerar en entitet finns den i databasen och kontexten spårar nu den befintliga entiteten
- Sedan använder vi SetValues för att ange värdena för alla egenskaper för den här entiteten till de som kom från klienten.
- SetValues-anropet markerar entiteten som ska uppdateras efter behov.
Tips/Råd
SetValues kommer endast att markera egenskaper som ändrade om dessa egenskaper har olika värden än de i den spårade entiteten. Det innebär att när uppdateringen skickas uppdateras endast de kolumner som faktiskt har ändrats. (Och om ingenting har ändrats skickas ingen uppdatering alls.)
Arbeta med grafer
Identitetslösning
Som nämnts ovan kan EF Core bara spåra en instans av en entitet med ett angivet primärnyckelvärde. När du arbetar med grafer bör grafen helst skapas så att den här invarianten underhålls och att kontexten endast ska användas för en arbetsenhet. Om grafen innehåller dubbletter måste du bearbeta grafen innan den skickas till EF för att konsolidera flera instanser till en. Detta kanske inte är trivialt där instanser har motstridiga värden och relationer, så att konsolidera dubbletter bör göras så snart som möjligt i din programpipeline för att undvika konfliktlösning.
Alla nya/alla befintliga entiteter
Ett exempel på hur du arbetar med grafer är att infoga eller uppdatera en blogg tillsammans med dess samling av associerade inlägg. Om alla entiteter i diagrammet ska infogas, eller om alla ska uppdateras, är processen densamma som beskrivs ovan för enskilda entiteter. Till exempel ett diagram över bloggar och inlägg som skapats så här:
var blog = new Blog
{
Url = "http://sample.com", Posts = new List<Post> { new Post { Title = "Post 1" }, new Post { Title = "Post 2" }, }
};
kan infogas så här:
public static async Task InsertGraph(DbContext context, object rootEntity)
{
context.Add(rootEntity);
await context.SaveChangesAsync();
}
Anropet till Lägg till markerar bloggen och alla inlägg som ska infogas.
På samma sätt kan uppdatering användas om alla entiteter i ett diagram behöver uppdateras:
public static async Task UpdateGraph(DbContext context, object rootEntity)
{
context.Update(rootEntity);
await context.SaveChangesAsync();
}
Bloggen och alla dess inlägg markeras som uppdaterade.
Blandning av nya och befintliga entiteter
Med automatiskt genererade nycklar kan Update återigen användas för både infogningar och uppdateringar, även om diagrammet innehåller en blandning av entiteter som kräver infogning och de som kräver uppdatering:
public static async Task InsertOrUpdateGraph(DbContext context, object rootEntity)
{
context.Update(rootEntity);
await context.SaveChangesAsync();
}
Uppdateringen markerar en entitet i diagrammet, bloggen eller inlägget för infogning om den inte har en nyckelvärdeuppsättning, medan alla andra entiteter har markerats för uppdatering.
Precis som tidigare, när du inte använder automatiskt genererade nycklar, kan en fråga och viss bearbetning användas:
public static async Task InsertOrUpdateGraph(BloggingContext context, Blog blog)
{
var existingBlog = await context.Blogs
.Include(b => b.Posts)
.FirstOrDefaultAsync(b => b.BlogId == blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
foreach (var post in blog.Posts)
{
var existingPost = existingBlog.Posts
.FirstOrDefault(p => p.PostId == post.PostId);
if (existingPost == null)
{
existingBlog.Posts.Add(post);
}
else
{
context.Entry(existingPost).CurrentValues.SetValues(post);
}
}
}
await context.SaveChangesAsync();
}
Hantera borttagningar
Det kan vara svårt att hantera borttagning eftersom avsaknaden av en entitet ofta innebär att den ska tas bort. Ett sätt att hantera detta är att använda "mjuka borttagningar" så att entiteten markeras som borttagen i stället för att faktiskt tas bort. Borttagningar blir sedan samma som uppdateringar. Mjuka borttagningar kan implementeras med hjälp av frågefilter.
För sanna borttagningar är ett vanligt mönster att använda ett tillägg av frågans mönster för att utföra vad som i huvudsak är en grafdifferens. Till exempel:
public static async Task InsertUpdateOrDeleteGraph(BloggingContext context, Blog blog)
{
var existingBlog = await context.Blogs
.Include(b => b.Posts)
.FirstOrDefaultAsync(b => b.BlogId == blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
foreach (var post in blog.Posts)
{
var existingPost = existingBlog.Posts
.FirstOrDefault(p => p.PostId == post.PostId);
if (existingPost == null)
{
existingBlog.Posts.Add(post);
}
else
{
context.Entry(existingPost).CurrentValues.SetValues(post);
}
}
foreach (var post in existingBlog.Posts)
{
if (!blog.Posts.Any(p => p.PostId == post.PostId))
{
context.Remove(post);
}
}
}
await context.SaveChangesAsync();
}
TrackGraph
Internt använder Lägg till, Bifoga och Uppdatera graftraversering för att avgöra för varje entitet om den ska markeras som Tillagd (för att lägga till), Ändrad (för att uppdatera), Oförändrad (gör inget) eller Raderad (för att radera). Den här mekanismen exponeras via TrackGraph-API:et. Anta till exempel att när klienten skickar tillbaka en graf med entiteter anger den en flagga för varje entitet som anger hur den ska hanteras. TrackGraph kan sedan användas för att bearbeta den här flaggan:
public static async Task SaveAnnotatedGraph(DbContext context, object rootEntity)
{
context.ChangeTracker.TrackGraph(
rootEntity,
n =>
{
var entity = (EntityBase)n.Entry.Entity;
n.Entry.State = entity.IsNew
? EntityState.Added
: entity.IsChanged
? EntityState.Modified
: entity.IsDeleted
? EntityState.Deleted
: EntityState.Unchanged;
});
await context.SaveChangesAsync();
}
Flaggorna visas bara som en del av entiteten för enkelhetens skull i exemplet. Flaggorna är vanligtvis en del av en DTO eller något annat tillstånd som ingår i begäran.