Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
ExecuteUpdate és az EF hagyományos változáskövetési ExecuteDelete és SaveChanges() -módszere nélkül is menthetők az adatok az adatbázisba. A két módszer bevezető összehasonlítása az adatok mentésének Áttekintés lapján található.
Törlés végrehajtása
Tegyük fel, hogy törölnie kell az összes olyan blogot, amely egy bizonyos küszöbérték alatti minősítéssel rendelkezik. A hagyományos SaveChanges() megközelítéshez a következőket kell tennie:
await foreach (var blog in context.Blogs.Where(b => b.Rating < 3).AsAsyncEnumerable())
{
context.Blogs.Remove(blog);
}
await context.SaveChangesAsync();
Ez meglehetősen nem hatékony módszer a feladat végrehajtására: lekérdezzük az adatbázist minden olyan bloghoz, amely megfelel a szűrőnknek, majd lekérdezzük, materializáljuk és nyomon követjük az összes ilyen példányt; az egyező entitások száma hatalmas lehet. Ezután jelezzük az EF változáskövetőnek, hogy minden egyes blogot el kell távolítani, és a változtatásokat a SaveChanges() hívással alkalmazzuk, amely mindegyikhez egy DELETE utasítást generál.
Az API-val ExecuteDelete végzett feladat a következő:
await context.Blogs.Where(b => b.Rating < 3).ExecuteDeleteAsync();
Ez a már jól ismert LINQ-operátorokkal határozza meg, hogy mely blogokat kell érinteni - ugyanúgy, mintha lekérdeznénk őket -, majd arra utasítja az EF-t, hogy hajtsa végre az SQL-t DELETE az adatbázison:
DELETE FROM [b]
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
Amellett, hogy egyszerűbb és rövidebb, ez nagyon hatékonyan fut az adatbázisban anélkül, hogy bármilyen adatot betöltenek az adatbázisból, vagy az EF változáskövetőjét is érintenék. Vegye figyelembe, hogy tetszőleges LINQ-operátorokkal kiválaszthatja, hogy mely blogokat szeretné törölni – ezeket a rendszer az SQL-nek fordítja le végrehajtás céljából az adatbázisban, mintha lekérdezné ezeket a blogokat.
FrissítésVégrehajtása
Ahelyett, hogy törölnénk ezeket a blogokat, mi lenne, ha módosítani szeretnénk egy tulajdonságot, hogy jelezzük, hogy inkább rejtve kellene lenniük?
ExecuteUpdate hasonló módon fejezi ki az SQL-utasítást UPDATE :
await context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdateAsync(setters => setters.SetProperty(b => b.IsVisible, false));
A ExecuteDelete-hoz hasonlóan először a LINQ segítségével meghatározzuk, hogy mely blogokat kell érinteni; de a ExecuteUpdate esetén a megfelelő blogokhoz alkalmazandó módosítást is ki kell fejeznünk. Ez SetProperty hívással úgy történik, hogy meghívja a ExecuteUpdate-t, és két argumentumot ad meg: a módosítani kívánt tulajdonságot (IsVisible) és az új értéket (false). Ez a következő SQL-t hajtja végre:
UPDATE [b]
SET [b].[IsVisible] = CAST(0 AS bit)
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
Több tulajdonság frissítése
ExecuteUpdate lehetővé teszi több tulajdonság frissítését egyetlen meghívásban. Például, ha azt szeretné, hogy a IsVisible hamisra és a Rating nullára legyen állítva, egyszerűen láncolja össze a további SetProperty hívásokat:
await context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdateAsync(setters => setters
.SetProperty(b => b.IsVisible, false)
.SetProperty(b => b.Rating, 0));
Ez a következő SQL-t hajtja végre:
UPDATE [b]
SET [b].[Rating] = 0,
[b].[IsVisible] = CAST(0 AS bit)
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
Hivatkozás a meglévő tulajdonságértékre
A fenti példák egy új állandó értékre frissítették a tulajdonságot.
ExecuteUpdate lehetővé teszi a meglévő tulajdonságérték hivatkozását az új érték kiszámításakor; Ha például az összes megfelelő blog minősítését egyenként szeretné növelni, használja a következőt:
await context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdateAsync(setters => setters.SetProperty(b => b.Rating, b => b.Rating + 1));
Vegye figyelembe, hogy a második argumentum a SetProperty most lambda függvény, és nem állandó, mint korábban volt. A b paraméter a frissített blogot jelöli, ezen belül a lambdában, b.Rating így a módosítás előtt tartalmazza a minősítést. Ez a következő SQL-t hajtja végre:
UPDATE [b]
SET [b].[Rating] = [b].[Rating] + 1
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
Navigációk és kapcsolódó entitások
ExecuteUpdate jelenleg nem támogatja a lambdán SetProperty belüli navigációkra való hivatkozásokat. Tegyük fel például, hogy frissíteni szeretnénk a blogok összes minősítését, hogy minden blog új minősítése az összes bejegyzés minősítésének átlaga legyen. Megpróbálhatjuk használni a ExecuteUpdate-t az alábbi módon:
await context.Blogs.ExecuteUpdateAsync(
setters => setters.SetProperty(b => b.Rating, b => b.Posts.Average(p => p.Rating)));
Az EF azonban lehetővé teszi ennek a műveletnek a végrehajtását azáltal, hogy először Select segítségével kiszámítja az átlagos minősítést, projektálja egy névtelen típusra, majd ezt alkalmazza ExecuteUpdate.
await context.Blogs
.Select(b => new { Blog = b, NewRating = b.Posts.Average(p => p.Rating) })
.ExecuteUpdateAsync(setters => setters.SetProperty(b => b.Blog.Rating, b => b.NewRating));
Ez a következő SQL-t hajtja végre:
UPDATE [b]
SET [b].[Rating] = CAST((
SELECT AVG(CAST([p].[Rating] AS float))
FROM [Post] AS [p]
WHERE [b].[Id] = [p].[BlogId]) AS int)
FROM [Blogs] AS [b]
Változások követése
Az ismerős SaveChanges felhasználók több módosítást is végrehajtanak, majd meghívják SaveChanges , hogy alkalmazzák ezeket a módosításokat az adatbázisra. Ezt az EF változáskövetője teszi lehetővé, amely összegyűjti - vagy nyomon követi - ezeket a módosításokat.
ExecuteUpdate és ExecuteDelete egészen másképp működnek: azonnal érvénybe lépnek, a meghívásuk időpontjában. Ez azt jelenti, hogy bár egyetlen ExecuteUpdate vagy ExecuteDelete művelet több sort is érinthet, nem lehet egyszerre több ilyen műveletet halmozni, például híváskor SaveChanges. Valójában a függvények egyáltalán nem ismerik az EF változáskövetőjét, és semmilyen módon nem működnek együtt vele. Ennek számos fontos következménye van.
Vegye figyelembe a következő kódot:
// 1. Query the blog with the name `SomeBlog`. Since EF queries are tracking by default, the Blog is now tracked by EF's change tracker.
var blog = await context.Blogs.SingleAsync(b => b.Name == "SomeBlog");
// 2. Increase the rating of all blogs in the database by one. This executes immediately.
await context.Blogs.ExecuteUpdateAsync(setters => setters.SetProperty(b => b.Rating, b => b.Rating + 1));
// 3. Increase the rating of `SomeBlog` by two. This modifies the .NET `Rating` property and is not yet persisted to the database.
blog.Rating += 2;
// 4. Persist tracked changes to the database.
await context.SaveChangesAsync();
Döntő fontosságú, hogy amikor ExecuteUpdate meghívják, és az összes blog frissül az adatbázisban, az EF változáskövetője nem frissül, és a nyomon követett .NET-példány továbbra is az eredeti minősítési értékkel rendelkezik, attól a ponttól kezdve, amikor lekérdezték. Tegyük fel, hogy a blog értékelése eredetileg 5 volt; a harmadik sor végrehajtása után az adatbázisban a minősítés most 6 (a ExecuteUpdate), míg a nyomon követett .NET-példányban a minősítés 7. A meghíváskor SaveChanges az EF észleli, hogy az új 7 érték eltér az eredeti 5 értéktől, és megőrzi a változást. A(z) ExecuteUpdate által elvégzett módosítás felülíródik, és nem kerül figyelembe vételre.
Ennek eredményeképpen általában jó ötlet elkerülni a nyomon követett SaveChanges módosítások és a nyomon nem követett módosítások keverését.ExecuteUpdate/ExecuteDelete
Tranzakciók
A fentiek folytatásaként fontos tudni, hogy amikor a ExecuteUpdate és ExecuteDelete meghívásra kerülnek, nem indul el implicit tranzakció. Vegye figyelembe a következő kódot:
await context.Blogs.ExecuteUpdateAsync(/* some update */);
await context.Blogs.ExecuteUpdateAsync(/* another update */);
var blog = await context.Blogs.SingleAsync(b => b.Name == "SomeBlog");
blog.Rating += 2;
await context.SaveChangesAsync();
Minden ExecuteUpdate hívás egyetlen SQL-t UPDATE küld az adatbázisba. Mivel nem jön létre tranzakció, ha valamilyen hiba megakadályozza a második ExecuteUpdate sikeres végrehajtását, az első hatása továbbra is megmarad az adatbázisban. Valójában a fenti négy művelet – két meghívás ExecuteUpdate, egy lekérdezés és SaveChanges – mindegyik saját tranzakcióján belül hajtódik végre. Ha több műveletet szeretne egyetlen tranzakcióba burkolni, explicit módon indítson el egy tranzakciót a következővel DatabaseFacade:
using (var transaction = context.Database.BeginTransaction())
{
context.Blogs.ExecuteUpdate(/* some update */);
context.Blogs.ExecuteUpdate(/* another update */);
...
}
A tranzakciókezeléssel kapcsolatos további információkért lásd a Tranzakciók használata című témakört.
Egyidejűség-vezérlés és érintett adatsorok
SaveChanges Automatikus egyidejűségi vezérlést biztosít egy egyidejűségi token használatával, amely garantálja, hogy a sor ne változzon meg a betöltés és a módosítások mentése között. Mivel ExecuteUpdate és ExecuteDelete nem használják a változáskövetőt, nem alkalmazhatják automatikusan az egyidejűség-vezérlést.
Mindkét módszer azonban a művelet által érintett sorok számát adja vissza; ez különösen hasznos lehet az egyidejűség-vezérlés implementálásához:
// (load the ID and concurrency token for a Blog in the database)
var numUpdated = await context.Blogs
.Where(b => b.Id == id && b.ConcurrencyToken == concurrencyToken)
.ExecuteUpdateAsync(/* ... */);
if (numUpdated == 0)
{
throw new Exception("Update failed!");
}
Ebben a kódban linq Where operátort használunk egy adott blog frissítésének alkalmazására, és csak akkor, ha az egyidejűségi jogkivonatnak van egy adott értéke (például az, amelyet a blog adatbázisból való lekérdezésekor láttunk). Ezután ellenőrizzük, hogy hány sor lett ténylegesen frissítve ExecuteUpdate; ha az eredmény nulla, nem frissültek sorok, és az egyidejűségi jogkivonat valószínűleg egy egyidejű frissítés eredményeként módosult.
Korlátozások
- Jelenleg csak a frissítés és a törlés támogatott; a beszúrást a következőn keresztül DbSet<TEntity>.AddSaveChanges()kell elvégezni:
- Bár az SQL UPDATE és DELETE utasítások lehetővé teszik az érintett sorok eredeti oszlopértékeinek lekérését, ezt jelenleg nem támogatja
ExecuteUpdateésExecuteDelete. - Ezen metódusok több meghívása nem kötegelhető. Minden egyes meghívás saját fordulót hajt végre, az adatbázisba.
- Az adatbázisok általában csak egyetlen táblát engedélyeznek az UPDATE vagy a DELETE használatával.
- Ezek a módszerek jelenleg csak relációsadatbázis-szolgáltatókkal működnek.
További erőforrások
-
.NET Data Access Community Standup ülés, ahol megvitatjuk
ExecuteUpdateésExecuteDelete.