Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Minden DbContext példány nyomon követi az entitások módosításait. Ezek a nyomon követett entitások viszont az adatbázis változásait hajtják végre, amikor SaveChanges meghívják őket.
Az Entity Framework Core (EF Core) változáskövetése akkor működik a legjobban, ha ugyanazt a példányt DbContext használják arra, hogy lekérdezze az entitásokat és frissítse őket a SaveChanges hívásával. Ennek az az oka, hogy az EF Core automatikusan nyomon követi a lekérdezett entitások állapotát, majd észleli az entitások módosításait a SaveChanges meghívásakor. Ezt a megközelítést az EF Core változáskövetése ismerteti.
Tip
Ez a dokumentum feltételezi, hogy az entitásállapotok és az EF Core-változáskövetés alapjai érthetőek. Ezekről a témakörökről további információt az EF Core változáskövetési funkciójában talál.
Tip
A mintakód GitHubról való letöltésével futtathatja és hibakeresést végezhet a dokumentum összes kódjában.
Introduction
Az entitások akár közvetlenül is "hozzácsatolhatók" egy DbContext-hoz, így a rendszer ezeket az entitásokat követi. Ez elsősorban akkor hasznos, ha:
- Új entitások létrehozása, amelyek be lesznek szúrva az adatbázisba.
- Egy másik DbContext-példány által korábban lekérdezett leválasztott entitások újbóli csatolása.
A legtöbb alkalmazásnak szüksége lesz ezek közül az elsőre, amelyet elsősorban a DbContext.Add módszerek kezelnek.
A másodikra csak olyan alkalmazásoknak van szüksége, amelyek módosítják az entitásokat vagy azok kapcsolatait, miközben az entitások nincsenek nyomon követve. Egy webalkalmazás például entitásokat küldhet a webes ügyfélnek, ahol a felhasználó módosítja az entitásokat, és visszaküldi az entitásokat. Ezeket az entitásokat "kapcsolat megszakított"-nak nevezzük, mivel eredetileg egy DbContextből lekérdezték őket, és a kliensnek történő elküldéskor megszakadt a kapcsolatuk ezzel a kontextussal.
A webalkalmazásnak most újra csatolnia kell ezeket az entitásokat, hogy ismét nyomon követhesse őket, és jelezze azokat a módosításokat, SaveChanges amelyek az adatbázis megfelelő frissítését teszik lehetővé. Ezt elsősorban a DbContext.Attach és DbContext.Update metódusok kezelik.
Tip
Az entitások csatolása ahhoz a DbContext-példányhoz, amelyből lekérdezték őket, általában nem szükséges. Ne hajtsa végre rutinszerűen a nyomkövetés nélküli lekérdezést, majd ne csatolja a visszaadott entitásokat ugyanahhoz a környezethez. Ez lassabb lesz, mint egy nyomkövetési lekérdezés használata, és olyan problémákat is okozhat, mint például az árnyéktulajdonságok hiányzó értékei, ami megnehezíti a helyes használatot.
Generált és explicit kulcsértékek
Alapértelmezés szerint az egész szám és a GUID kulcs tulajdonságaiautomatikusan generált kulcsértékek használatára vannak konfigurálva. Ennek jelentős előnye van a változáskövetésben: egy nem beállított kulcsérték azt jelzi, hogy az entitás "új". Az "új" alatt azt értjük, hogy még nem lett beszúrva az adatbázisba.
A következő szakaszokban két modellt használunk. Az első úgy van konfigurálva, hogy ne használjon generált kulcsértékeket:
public class Blog
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name { get; set; }
public IList<Post> Posts { get; } = new List<Post>();
}
public class Post
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int? BlogId { get; set; }
public Blog Blog { get; set; }
}
A nem generált (azaz explicit módon beállított) kulcsértékek minden példában elsőként jelennek meg, mivel minden nagyon explicit és könnyen követhető. Ezt követően egy példa követi, amelyben a generált kulcsértékeket használja a rendszer:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int? BlogId { get; set; }
public Blog Blog { get; set; }
}
Figyelje meg, hogy ebben a modellben a kulcstulajdonságoknak nincs szükségük további konfigurációra, mivel a generált kulcsértékek használata az egyszerű egész számok kulcsainak alapértelmezett beállítása.
Új entitások beszúrása
Explicit kulcsértékek
Egy entitást a Added állapotban kell követni, hogy SaveChanges által beszúrható legyen. Az entitások általában hozzáadott állapotba kerülnek, ha meghívják az egyik DbContext.Add, DbContext.AddRange, DbContext.AddAsync, DbContext.AddRangeAsync metódust, vagy az azzal egyenértékű DbSet<TEntity> metódust.
Tip
Ezek a módszerek mind ugyanúgy működnek a változáskövetés kontextusában. További információkért tekintse meg a további változáskövetési funkciókat .
Például egy új blog követésének megkezdéséhez:
context.Add(
new Blog { Id = 1, Name = ".NET Blog", });
A hívás utáni változáskövető hibakeresési nézet vizsgálata azt mutatja, hogy a környezet az új entitás Added állapotát követi nyomon:
Blog {Id: 1} Added
Id: 1 PK
Name: '.NET Blog'
Posts: []
Az Add metódusok azonban nem csak egy adott entitáson működnek. Valójában elkezdik nyomon követni a kapcsolódó entitások teljes gráfját, és mindet az Added állapotba helyezik. Ha például be szeretne szúrni egy új blogot és a hozzá tartozó új bejegyzéseket:
context.Add(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
A környezet most a következő módon követi nyomon az összes entitást Added:
Blog {Id: 1} Added
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Added
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Added
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Figyelje meg, hogy a fenti példákban explicit értékek lettek beállítva a Id kulcstulajdonságokhoz. Ennek az az oka, hogy a modell úgy lett konfigurálva, hogy explicit módon állítsa be a kulcsértékeket ahelyett, hogy automatikusan generálta volna a kulcsértékeket. Ha nem használ generált kulcsokat, a kulcstulajdonságokat kifejezetten be kell állítani a hívás Add. Ezek a kulcsértékek a SaveChanges meghívásakor lesznek beszúrva. Például az SQLite használatakor:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Blogs" ("Id", "Name")
VALUES (@p0, @p1);
-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String), @p3='1' (DbType = String), @p4='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p5='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title")
VALUES (@p2, @p3, @p4, @p5);
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String), @p1='1' (DbType = String), @p2='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p3='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2, @p3);
Ezek az entitások a Unchanged SaveChanges befejeződése után az állapotban lesznek nyomon követve, mivel ezek az entitások már léteznek az adatbázisban:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Generált kulcsértékek
Ahogy fentebb említettük, az egész szám és a GUID-kulcs tulajdonságai alapértelmezés szerint automatikusan generált kulcsértékek használatára vannak konfigurálva. Ez azt jelenti, hogy az alkalmazás nem állíthat be külön kulcsértéket. Ha például be szeretne szúrni egy új blogot és bejegyzéseket, mindegyiket generált kulcsértékekkel:
context.Add(
new Blog
{
Name = ".NET Blog",
Posts =
{
new Post
{
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
Az explicit kulcsértékekhez hasonlóan a környezet most is a következő módon követi nyomon ezeket az entitásokat Added:
Blog {Id: -2147482644} Added
Id: -2147482644 PK Temporary
Name: '.NET Blog'
Posts: [{Id: -2147482637}, {Id: -2147482636}]
Post {Id: -2147482637} Added
Id: -2147482637 PK Temporary
BlogId: -2147482644 FK Temporary
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: -2147482644}
Post {Id: -2147482636} Added
Id: -2147482636 PK Temporary
BlogId: -2147482644 FK Temporary
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: -2147482644}
Ebben az esetben figyelje meg, hogy az egyes entitásokhoz ideiglenes kulcsértékek lettek létrehozva. Ezeket az értékeket az EF Core használja a SaveChanges meghívásáig, és ekkor a rendszer visszaolvassa a valós kulcsértékeket az adatbázisból. Például az SQLite használatakor:
-- Executed DbCommand (0ms) [Parameters=[@p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Blogs" ("Name")
VALUES (@p0);
SELECT "Id"
FROM "Blogs"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p2='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p3='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p1, @p2, @p3);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
A SaveChanges befejeződése után az összes entitás frissült a valós kulcsértékekkel, és az Unchanged állapotban lesznek nyomon követve, mivel most már megegyeznek az adatbázisban lévő állapottal:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Ez pontosan ugyanaz a végállapot, mint az előző példában, amely explicit kulcsértékeket használt.
Tip
A explicit kulcsértékek akkor is beállíthatók, ha generált kulcsértékeket használnak. Ezután az EF Core megpróbálja a kulcsértékkel elvégezni a beszúrást. Egyes adatbáziskonfigurációk, például az identitásoszlopokkal rendelkező SQL Server nem támogatják az ilyen beszúrásokat, és eldobják őket (kerülő megoldásért tekintse meg ezeket a dokumentumokat).
Meglévő entitások csatolása
Explicit kulcsértékek
A lekérdezésekből visszaadott entitásokat a rendszer az Unchanged állapotában követi nyomon. Az Unchanged állapot azt jelenti, hogy az entitás nem lett módosítva a lekérdezés óta. A leválasztott entitás, amelyet esetleg egy HTTP-kérésben egy webes ügyféltől kaptak vissza, ebbe az állapotba helyezhető vagy DbContext.Attach, vagy DbContext.AttachRange, illetve a DbSet<TEntity> megfelelő metódusainak segítségével. Például egy meglévő blog követésének megkezdéséhez:
context.Attach(
new Blog { Id = 1, Name = ".NET Blog", });
Note
Az alábbi példákban kifejezetten az egyszerűség kedvéért new hozunk létre entitásokat. Az entitáspéldányok általában más forrásból szoktak származni, például deszerializálásra kerülnek egy kliensből, vagy egy HTTP POST kérés adatai alapján jönnek létre.
A hívás utáni változáskövető hibakeresési nézet vizsgálata azt mutatja, hogy az entitás az állapotban Unchanged van nyomon követve:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: []
Akárcsak Add, Attach a csatlakoztatott entitások teljes gráfját is beállítja az Unchanged állapothoz. Például egy meglévő blog és a hozzá tartozó meglévő bejegyzések csatolása:
context.Attach(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
A környezet most a következő módon követi nyomon az összes entitást Unchanged:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
A SaveChanges hívásának jelenleg nincs hatása. Az összes entitás meg van jelölve Unchanged, így az adatbázisban nincs frissíteni való.
Generált kulcsértékek
Ahogy fentebb említettük, az egész szám és a GUID-kulcs tulajdonságai alapértelmezés szerint automatikusan generált kulcsértékek használatára vannak konfigurálva. Ez a leválasztott entitások használata esetén jelentős előnyt jelent: a nem létező kulcsérték azt jelzi, hogy az entitás még nem lett beszúrva az adatbázisba. Ez lehetővé teszi, hogy a változáskövető automatikusan észlelje az új entitásokat, és állapotba helyezze őket Added . Tegyük fel például, hogy egy blog és bejegyzés gráfját csatolja:
context.Attach(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
},
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
},
}
});
A blog kulcsértéke 1, ami azt jelzi, hogy már létezik az adatbázisban. Két bejegyzés is rendelkezik kulcsértékekkel, de a harmadik nem. Az EF Core ezt a kulcsértéket 0 értékként látja, amely egy egész szám alapértelmezett CLR-értéke. Ez azt eredményezi, hogy az EF Core az új entitást Added-ként jelöli meg ahelyett, hogy Unchanged lenne.
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482636}]
Post {Id: -2147482636} Added
Id: -2147482636 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 includes many enhancements, including single file a...'
Title: 'Announcing .NET 5.0'
Blog: {Id: 1}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
A SaveChanges meghívása jelenleg semmit nem tesz az Unchanged entitásokkal, de beszúrja az új entitást az adatbázisba. Például az SQLite használatakor:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size = 19)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
Itt fontos megfigyelni, hogy a generált kulcsértékekkel az EF Core képes automatikusan megkülönböztetni az újat a leválasztott gráf meglévő entitásaitól. Dióhéjban a generált kulcsok használatakor az EF Core mindig beszúr egy entitást, ha az entitás nem rendelkezik kulcsértékkészlettel.
Meglévő entitások frissítése
Explicit kulcsértékek
DbContext.Update, DbContext.UpdateRange, és az ezekhez tartozó egyenértékű metódusok DbSet<TEntity> pontosan úgy viselkednek, mint a fent leírt Attach módszerek, azzal a különbséggel, hogy az entitások nem az Modified állapotba, hanem az Unchanged állapotba kerülnek. Egy meglévő blog nyomon követésének megkezdése például a következőként Modified:
context.Update(
new Blog { Id = 1, Name = ".NET Blog", });
A hívás utáni változáskövetési hibakeresési nézet vizsgálata azt mutatja, hogy a környezet az entitás Modified állapotát követi nyomon:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog' Modified
Posts: []
Pontosan ahogyan a Add és Attach esetében, a Update tulajdonképpen az összes kapcsolódó entitás Modified jelöli meg. Ha például egy meglévő blogot szeretne csatolni, és a kapcsolódó meglévő bejegyzéseket a következőként Modifiedszeretné csatolni:
context.Update(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
A környezet most a következő módon követi nyomon az összes entitást Modified:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog' Modified
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Modified
Id: 1 PK
BlogId: 1 FK Modified Originally <null>
Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified
Title: 'Announcing the Release of EF Core 5.0' Modified
Blog: {Id: 1}
Post {Id: 2} Modified
Id: 2 PK
BlogId: 1 FK Modified Originally <null>
Content: 'F# 5 is the latest version of F#, the functional programming...' Modified
Title: 'Announcing F# 5' Modified
Blog: {Id: 1}
A SaveChanges ezen a ponton történő meghívásával a rendszer frissítéseket küld az adatbázisba az összes ilyen entitás esetében. Például az SQLite használatakor:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String), @p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();
Generált kulcsértékek
Attach A generált kulcsértékeknek is ugyanaz a fő előnye, mint a Update: a nem beállított kulcsérték azt jelzi, hogy az entitás új, és még nem lett beillesztve az adatbázisba. Ahogyan a Attach esetében, ez lehetővé teszi a DbContext számára, hogy automatikusan észlelje az új entitásokat, és Added állapotba helyezze őket. Fontolja meg például a blogok és bejegyzések ezen gráfjának meghívását Update :
context.Update(
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
},
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
},
}
});
A példához hasonlóan a Attach rendszer a kulcsérték nélküli bejegyzést is újként észleli, és az állapotra állítja Added . A többi entitás a következőként Modifiedvan megjelölve:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog' Modified
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482633}]
Post {Id: -2147482633} Added
Id: -2147482633 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 includes many enhancements, including single file a...'
Title: 'Announcing .NET 5.0'
Blog: {Id: 1}
Post {Id: 1} Modified
Id: 1 PK
BlogId: 1 FK Modified Originally <null>
Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified
Title: 'Announcing the Release of EF Core 5.0' Modified
Blog: {Id: 1}
Post {Id: 2} Modified
Id: 2 PK
BlogId: 1 FK Modified Originally <null>
Content: 'F# 5 is the latest version of F#, the functional programming...' Modified
Title: 'Announcing F# 5' Modified
Blog: {Id: 1}
Ezen a ponton a SaveChanges hívása frissítéseket küld az adatbázisba az összes meglévő entitásra, miközben az új entitás beillesztésre kerül. Például az SQLite használatakor:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)], CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String), @p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size = 19)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
Ez egy nagyon egyszerű módja annak, hogy frissítéseket és beszúrásokat hozzon létre egy leválasztott gráfból. Ez azonban azt eredményezi, hogy a rendszer frissítéseket vagy beszúrásokat küld az adatbázisba minden követett entitás minden tulajdonságához, még akkor is, ha egyes tulajdonságértékek nem módosultak. Ne ijedjen meg tőle; sok kis gráfot tartalmazó alkalmazás esetén ez egy egyszerű és pragmatikus módszer a frissítések generálására. Ez azt jelenti, hogy más összetettebb minták néha hatékonyabb frissítéseket eredményezhetnek az EF Core identitásfeloldásában leírtak szerint.
Meglévő entitások törlése
Ahhoz, hogy egy entitást a SaveChanges töröljön, az Deleted állapotában kell nyomon követni. Az entitások jellemzően az Deleted állapotba kerülnek azzal, hogy meghívják az DbContext.Remove, DbContext.RemoveRange vagy az ezekkel egyenértékű metódusokat a DbSet<TEntity>-on. Egy meglévő bejegyzés megjelölése például a következőként Deleted:
context.Remove(
new Post { Id = 2 });
A hívás utáni változáskövetési hibakeresési nézet vizsgálata azt mutatja, hogy a környezet az entitás Deleted állapotát követi nyomon:
Post {Id: 2} Deleted
Id: 2 PK
BlogId: <null> FK
Content: <null>
Title: <null>
Blog: <null>
Ez az entitás a SaveChanges meghívásakor törlődik. Például az SQLite használatakor:
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
A SaveChanges befejezése után a törölt entitás le lesz választva a DbContext elemről, mivel már nem létezik az adatbázisban. A hibakeresési nézet ezért üres, mert nincsenek nyomon követve entitások.
Függő/gyermek entitások törlése
A függő/gyermek entitások gráfból való törlése egyszerűbb, mint a fő/szülő entitások törlése. További információért tekintse meg a következő szakaszt , valamint az Idegen kulcsok és navigációk módosítása című szakaszt .
Szokatlan, hogy Remove függvényt hívunk meg egy new segítségével létrehozott entitáson. Ezenkívül, a Add, Attach és Update helyzetével ellentétben, nem gyakori, hogy olyan entitást hívunk meg Remove, amely még nincs nyomon követve a Unchanged vagy Modified állapotban. Ehelyett általában a kapcsolódó entitások egyetlen entitását vagy gráfját követi nyomon, majd meghívja Remove a törölni kívánt entitásokat. A nyomon követett entitások grafikonját általában a következők hozzák létre:
- Lekérdezés futtatása az entitásokhoz
- A leválasztott entitások gráfján a
AttachvagyUpdatemetódusok használata, az előző szakaszokban leírtak szerint.
Az előző szakaszban szereplő kód például nagyobb valószínűséggel szerez be egy bejegyzést egy ügyféltől, majd az alábbihoz hasonlót hajt végre:
context.Attach(post);
context.Remove(post);
Ez pontosan ugyanúgy viselkedik, mint az előző példában, mivel egy nem nyomon követett entitást Remove meghívása először hozzácsatolja, majd megjelöli Deleted.
Reálisabb példákban először egy entitásdiagramot csatolunk, majd néhány entitást töröltként jelölünk meg. Például:
// Attach a blog and associated posts
context.Attach(blog);
// Mark one post as Deleted
context.Remove(blog.Posts[1]);
Az összes entitás a következőként Unchangedvan megjelölve, kivéve azt, amelyiken Remove meghívták:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Deleted
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Ez az entitás a SaveChanges meghívásakor törlődik. Például az SQLite használatakor:
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
A SaveChanges befejezése után a törölt entitás le lesz választva a DbContext elemről, mivel már nem létezik az adatbázisban. Más entitások továbbra is az Unchanged állapotban maradnak:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Fő/szülő entitások törlése
A két entitástípust összekötő kapcsolatok mindegyikének van egy fő vagy szülő vége, valamint egy függő vagy gyermek vége. A függő/gyermek entitás az idegen kulcs tulajdonsággal rendelkező entitás. Az egy-a-többhöz kapcsolatban a fő/szülő az "egy" oldalon, a függő/gyermek pedig a "több" oldalon van. További információt a Kapcsolatok című témakörben talál.
Az előző példákban töröltünk egy bejegyzést, amely egy kapcsolt/gyermek entitás a blogbejegyzések egy-a-többhöz kapcsolat részeként. Ez viszonylag egyszerű, mivel egy függő/gyermek entitás eltávolítása semmilyen hatással nincs más entitásokra. Másrészt a fő/szülő entitás törlésének hatással kell lennie a függő/gyermek entitásokra is. Ha ezt nem teszi meg, az idegenkulcs-érték egy már nem létező elsődleges kulcsértékre hivatkozik. Ez érvénytelen modellállapot, és hivatkozási kényszerhibát eredményez a legtöbb adatbázisban.
Ez az érvénytelen modellállapot kétféleképpen kezelhető:
- FK-értékek beállítása null értékre. Ez azt jelzi, hogy az eltartottak/gyermekek már nem kapcsolódnak egyetlen taghoz/szülőhöz sem. Ez az alapértelmezett olyan választható kapcsolatok esetében, ahol az idegen kulcsnak null értékűnek kell lennie. Az FK null értékre állítása nem érvényes a szükséges kapcsolatok esetében, ahol az idegen kulcs általában nem null értékű.
- A függők/gyermekek törlése. Ez a kötelező kapcsolatok alapértelmezett értéke, és az opcionális kapcsolatokra is érvényes.
A változáskövetéssel és a kapcsolatokkal kapcsolatos részletes információkért tekintse meg az Idegen kulcsok és navigációk módosítása című témakört.
Választható kapcsolatok
Az Post.BlogId idegenkulcs-tulajdonság null értékű a használt modellben. Ez azt jelenti, hogy a kapcsolat nem kötelező, ezért az EF Core alapértelmezett viselkedése az, hogy a blog törlésekor az idegen kulcstulajdonságokat null értékre állítja BlogId . Például:
// Attach a blog and associated posts
context.Attach(blog);
// Mark the blog as deleted
context.Remove(blog);
A változáskövető hibakeresési nézetének vizsgálata a hívás Remove után azt mutatja, hogy a blog a vártnak megfelelően a következőként Deletedvan megjelölve:
Blog {Id: 1} Deleted
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Modified
Id: 1 PK
BlogId: <null> FK Modified Originally 1
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: <null>
Post {Id: 2} Modified
Id: 2 PK
BlogId: <null> FK Modified Originally 1
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>
Még érdekesebb, hogy az összes kapcsolódó bejegyzés most meg van jelölve Modified. Ennek az az oka, hogy az egyes entitások idegenkulcs-tulajdonsága null értékűre lett beállítva. A SaveChanges hívása minden bejegyzés idegenkulcs-értékét null értékre frissíti az adatbázisban, majd törli a blogot:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p1='2' (DbType = String), @p0=NULL], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Blogs"
WHERE "Id" = @p2;
SELECT changes();
A SaveChanges befejezése után a törölt entitás le lesz választva a DbContext elemről, mivel már nem létezik az adatbázisban. Az egyéb entitások mostantól null értékű idegen kulcsértékekkel vannak megjelölve Unchanged , amelyek megegyeznek az adatbázis állapotával:
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: <null> FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: <null>
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: <null> FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>
Szükséges kapcsolatok
Ha az Post.BlogId idegenkulcs-tulajdonság nem null értékű, akkor a blogok és a bejegyzések közötti kapcsolat "kötelezővé" válik. Ebben az esetben az EF Core alapértelmezés szerint törli a függő/gyermek entitásokat, amikor a fő/szülő entitást törlik. Például töröl egy kapcsolódó bejegyzéseket tartalmazó blogot az előző példához hasonlóan:
// Attach a blog and associated posts
context.Attach(blog);
// Mark the blog as deleted
context.Remove(blog);
A változáskövető hibakeresési nézetének vizsgálata a hívás Remove után azt mutatja, hogy a várt módon a blog ismét a következőként Deletedvan megjelölve:
Blog {Id: 1} Deleted
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Deleted
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Deleted
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Érdekesebb ebben az esetben az, hogy az összes kapcsolódó bejegyzést is Deleted-ként megjelölték. A SaveChanges meghívásával a blog és az összes kapcsolódó bejegyzés törölve lesz az adatbázisból:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
DELETE FROM "Blogs"
WHERE "Id" = @p1;
A SaveChanges befejezése után az összes törölt entitás le lesz választva a DbContextről, mivel már nem léteznek az adatbázisban. A hibakeresési nézet kimenete ezért üres.
Note
Ez a dokumentum csak az EF Core kapcsolatok használatakor karcolja meg a felszínt. A Kapcsolatok című részben talál további információkat a kapcsolatok modellezéséről, az Idegen kulcsok és navigációs útvonalak megváltoztatása című részben pedig arról, hogyan lehet frissíteni vagy törölni a függő/gyermek entitásokat a SaveChanges híváskor.
Egyéni nyomon követés a TrackGraph használatával
ChangeTracker.TrackGraph úgy működik, mint Add, Attach és Update azzal a kivételrel, hogy minden entitáspéldányhoz visszahívást hoz létre a nyomon követés előtt. Ez lehetővé teszi az egyéni logika használatát az egyes entitások gráfokban való nyomon követésének meghatározásához.
Vegyük például azt a szabályt, amelyet az EF Core használ az entitások generált kulcsértékekkel való nyomon követéséhez: ha a kulcs értéke nulla, akkor az entitás új, és be kell szúrni. Bővítsük ki ezt a szabályt annak kimondására, hogy a kulcs értéke negatív-e, akkor az entitást törölni kell. Ez lehetővé teszi a leválasztott gráf entitásainak elsődleges kulcsértékeinek módosítását a törölt entitások megjelöléséhez:
blog.Posts.Add(
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
}
);
var toDelete = blog.Posts.Single(e => e.Title == "Announcing F# 5");
toDelete.Id = -toDelete.Id;
Ez a leválasztott gráf ezután nyomon követhető a TrackGraph használatával:
public static async Task UpdateBlog(Blog blog)
{
using var context = new BlogsContext();
context.ChangeTracker.TrackGraph(
blog, node =>
{
var propertyEntry = node.Entry.Property("Id");
var keyValue = (int)propertyEntry.CurrentValue;
if (keyValue == 0)
{
node.Entry.State = EntityState.Added;
}
else if (keyValue < 0)
{
propertyEntry.CurrentValue = -keyValue;
node.Entry.State = EntityState.Deleted;
}
else
{
node.Entry.State = EntityState.Modified;
}
Console.WriteLine($"Tracking {node.Entry.Metadata.DisplayName()} with key value {keyValue} as {node.Entry.State}");
});
await context.SaveChangesAsync();
}
A gráf minden entitása esetében a fenti kód ellenőrzi az elsődleges kulcs értékét az entitás nyomon követése előtt. Nem beállított (nulla) kulcsértékek esetén a kód azt teszi, amit az EF Core általában tenne. Vagyis ha a kulcs nincs beállítva, akkor az entitás a következőképpen van megjelölve Added. Ha a kulcs be van állítva, és az érték nem negatív, akkor az entitás a következőképpen van megjelölve Modified. Ha azonban negatív kulcsértéket talál, akkor a rendszer visszaállítja a valós, nem negatív értékét, és az entitást a rendszer a következőképpen követi nyomon Deleted.
A kód futtatásának kimenete a következő:
Tracking Blog with key value 1 as Modified
Tracking Post with key value 1 as Modified
Tracking Post with key value -2 as Deleted
Tracking Post with key value 0 as Added
Note
Az egyszerűség kedvéért ez a kód feltételezi, hogy minden entitáshoz tartozik egy egész szám elsődleges kulcs tulajdonsága.Id Ez egy absztrakt alaposztályba vagy interfészbe kodifikálható. Másik lehetőségként az elsődleges kulcstulajdonság vagy tulajdonságok lekérthetők a IEntityType metaadatokból, így ez a kód bármilyen típusú entitással működne.
A TrackGraphnak két túlterhelése van. A fenti egyszerű túlterhelésben az EF Core határozza meg, hogy mikor kell leállítani a gráfon való áthaladást. Pontosabban leállítja az új kapcsolódó entitások látogatását egy adott entitásból, ha az entitás már nyomon van követve, vagy ha a visszahívás nem kezdi nyomon követni az entitást.
A speciális túlterhelésnek ChangeTracker.TrackGraph<TState>(Object, TState, Func<EntityEntryGraphNode<TState>,Boolean>)van egy visszahívása, amely egy bool-t ad vissza. Ha a visszahívás hamis eredményt ad vissza, akkor a gráf bejárása leáll, ellenkező esetben folytatódik. Ennek a túlterhelésnek a használatakor érdemes ügyelni a végtelen hurkok elkerülésére.
A speciális túlterhelés azt is lehetővé teszi, hogy az állapotot a TrackGraphnak adja meg, és ezt az állapotot a rendszer minden visszahívásnak átadja.