Delen via


Toegang tot bijgehouden entiteiten

Er zijn vier belangrijkste API's voor toegang tot entiteiten die worden bijgehouden door een DbContext:

Elk van deze wordt gedetailleerder beschreven in de onderstaande secties.

Aanbeveling

In dit document wordt ervan uitgegaan dat entiteitsstatussen en de basisprincipes van EF Core-wijzigingen bijhouden worden begrepen. Zie Wijzigingen bijhouden in EF Core voor meer informatie over deze onderwerpen.

Aanbeveling

U kunt alle code in dit document uitvoeren en fouten opsporen door de voorbeeldcode van GitHub te downloaden.

DbContext.Entry- en EntityEntry-exemplaren gebruiken

Voor elke bijgehouden entiteit houdt Entity Framework Core (EF Core) het volgende bij:

  • De algehele status van de entiteit. Dit is een van Unchanged, Modifiedof Added, of Deleted; zie Wijzigingen bijhouden in EF Core voor meer informatie.
  • De relaties tussen bijgehouden entiteiten. Bijvoorbeeld de blog waartoe een bericht behoort.
  • De 'huidige waarden' van eigenschappen.
  • De 'oorspronkelijke waarden' van eigenschappen, wanneer deze informatie beschikbaar is. Oorspronkelijke waarden zijn de eigenschapswaarden die bestonden toen een entiteit uit de database werd opgevraagd.
  • Welke eigenschapswaarden zijn gewijzigd sinds ze zijn opgevraagd.
  • Andere informatie over eigenschapswaarden, zoals of de waarde tijdelijk is.

Het doorgeven van een entiteitsexemplaar aan DbContext.Entry resulteert in een EntityEntry<TEntity> dat toegang biedt tot deze informatie voor de betreffende entiteit. Voorbeeld:

using var context = new BlogsContext();

var blog = await context.Blogs.SingleAsync(e => e.Id == 1);
var entityEntry = context.Entry(blog);

In de volgende secties ziet u hoe u een EntityEntry gebruikt om de entiteitsstatus te openen en bewerken, evenals de status van de eigenschappen en navigatie van de entiteit.

Werken met de entiteit

Het meest voorkomende gebruik is EntityEntry<TEntity> om toegang te krijgen tot de huidige EntityState van een entiteit. Voorbeeld:

var currentState = context.Entry(blog).State;
if (currentState == EntityState.Unchanged)
{
    context.Entry(blog).State = EntityState.Modified;
}

De invoermethode kan ook worden gebruikt voor entiteiten die nog niet worden bijgehouden. Hiermee wordt de entiteit niet bijgehouden. de status van de entiteit is nog steeds Detached. De geretourneerde EntityEntry kan echter worden gebruikt om de entiteitsstatus te wijzigen, waarna de entiteit in de opgegeven status wordt bijgehouden. Bijvoorbeeld, de volgende code zal een blogexemplaar volgen als Added:

var newBlog = new Blog();
Debug.Assert(context.Entry(newBlog).State == EntityState.Detached);

context.Entry(newBlog).State = EntityState.Added;
Debug.Assert(context.Entry(newBlog).State == EntityState.Added);

Aanbeveling

In tegenstelling tot ef6 zorgt het instellen van de status van een afzonderlijke entiteit ervoor dat niet alle verbonden entiteiten worden bijgehouden. Dit maakt het instellen van de status op deze manier een bewerking op lager niveau dan het aanroepen Add, Attachof Update, die op een hele grafiek van entiteiten werken.

De volgende tabel bevat een overzicht van de manieren waarop u een EntityEntry kunt gebruiken om met een hele entiteit te werken:

EntityEntry-lid Beschrijving
EntityEntry.State Hiermee haalt u de EntityState entiteit op en stelt u deze in.
EntityEntry.Entity Hiermee wordt het entiteitsexemplaar opgehaald.
EntityEntry.Context De DbContext die deze entiteit volgt.
EntityEntry.Metadata IEntityType metagegevens voor het type entiteit.
EntityEntry.IsKeySet Of de entiteit de sleutelwaarde al dan niet heeft ingesteld.
EntityEntry.Reload() Overschrijft eigenschapswaarden met waarden die uit de database worden gelezen.
EntityEntry.DetectChanges() Hiermee wordt de detectie van wijzigingen voor alleen deze entiteit afgedwongen; zie Wijzigingsdetectie en meldingen.

Werken met één eigenschap

Verschillende overbelastingen van EntityEntry<TEntity>.Property bieden toegang tot informatie over een individuele eigenschap van een entiteit. Gebruik bijvoorbeeld een sterk getypte, fluent-achtige API:

PropertyEntry<Blog, string> propertyEntry = context.Entry(blog).Property(e => e.Name);

De naam van de eigenschap kan in plaats daarvan worden doorgegeven als een tekenreeks. Voorbeeld:

PropertyEntry<Blog, string> propertyEntry = context.Entry(blog).Property<string>("Name");

De geretourneerde PropertyEntry<TEntity,TProperty> gegevens kunnen vervolgens worden gebruikt voor toegang tot informatie over de eigenschap. Het kan bijvoorbeeld worden gebruikt om de huidige waarde van de eigenschap op deze entiteit op te halen en in te stellen:

string currentValue = context.Entry(blog).Property(e => e.Name).CurrentValue;
context.Entry(blog).Property(e => e.Name).CurrentValue = "1unicorn2";

Beide property-methoden, die hierboven worden gebruikt, retourneren een sterk getypeerde generieke PropertyEntry<TEntity,TProperty> instantie. Het gebruik van dit algemene type heeft de voorkeur omdat hiermee toegang wordt gegeven tot eigenschapswaarden zonder bokswaardetypen. Als het type entiteit of eigenschap echter niet bekend is tijdens het compileren, kan in plaats daarvan een niet-algemeen PropertyEntry exemplaar worden verkregen:

PropertyEntry propertyEntry = context.Entry(blog).Property("Name");

Hiermee heeft u toegang tot eigenschapsgegevens voor elke eigenschap, ongeacht het type, ten koste van waardetypen voor boksen. Voorbeeld:

object blog = await context.Blogs.SingleAsync(e => e.Id == 1);

object currentValue = context.Entry(blog).Property("Name").CurrentValue;
context.Entry(blog).Property("Name").CurrentValue = "1unicorn2";

De volgende tabel bevat een overzicht van eigenschapsgegevens die worden weergegeven door PropertyEntry:

PropertyEntry-lid Beschrijving
PropertyEntry<TEntity,TProperty>.CurrentValue Hiermee haalt u de huidige waarde van de eigenschap op en stelt u deze in.
PropertyEntry<TEntity,TProperty>.OriginalValue Hiermee wordt, indien beschikbaar, de oorspronkelijke waarde van de eigenschap opgehaald en ingesteld.
PropertyEntry<TEntity,TProperty>.EntityEntry Een terugverwijzing naar de EntityEntry<TEntity>-entiteit.
PropertyEntry.Metadata IProperty metagegevens voor de eigenschap.
PropertyEntry.IsModified Hiermee wordt aangegeven of deze eigenschap is gemarkeerd als gewijzigd en kan deze status worden gewijzigd.
PropertyEntry.IsTemporary Hiermee wordt aangegeven of deze eigenschap is gemarkeerd als tijdelijk en kan deze status worden gewijzigd.

Opmerkingen:

  • De oorspronkelijke waarde van een eigenschap is de waarde die de eigenschap had toen de entiteit werd opgevraagd uit de database. Oorspronkelijke waarden zijn echter niet beschikbaar als de verbinding met de entiteit is verbroken en vervolgens expliciet is gekoppeld aan een andere DbContext, bijvoorbeeld met Attach of Update. In dit geval is de oorspronkelijke geretourneerde waarde hetzelfde als de huidige waarde.
  • SaveChanges werkt alleen de eigenschappen bij die zijn gemarkeerd als gewijzigd. Stel IsModified in op True om EF Core te dwingen een bepaalde eigenschapswaarde bij te werken of stel deze in op false om te voorkomen dat EF Core de eigenschapswaarde bijwerkt.
  • Tijdelijke waarden worden doorgaans gegenereerd door EF Core-waardegeneratoren. Als u de huidige waarde van een eigenschap instelt, wordt de tijdelijke waarde vervangen door de opgegeven waarde en wordt de eigenschap als niet tijdelijk gemarkeerd. Ingesteld IsTemporary op True om af te dwingen dat een waarde tijdelijk is, zelfs nadat deze expliciet is ingesteld.

Werken met één navigatie

Verschillende overbelastingen van EntityEntry<TEntity>.Reference, EntityEntry<TEntity>.Collectionen EntityEntry.Navigation bieden toegang tot informatie over een afzonderlijke navigatie.

Verwijzingsnavigaties naar één gerelateerde entiteit worden geopend via de Reference methoden. Verwijzingsnavigatie wijst naar de 'een'-zijden van een-op-veel-relaties en beide zijden van een-op-een-relaties. Voorbeeld:

ReferenceEntry<Post, Blog> referenceEntry1 = context.Entry(post).Reference(e => e.Blog);
ReferenceEntry<Post, Blog> referenceEntry2 = context.Entry(post).Reference<Blog>("Blog");
ReferenceEntry referenceEntry3 = context.Entry(post).Reference("Blog");

Navigatie-elementen kunnen ook verzamelingen zijn van gerelateerde entiteiten wanneer ze worden gebruikt voor de "veel" aspecten van een-op-veel- en veel-op-veel-relaties. De Collection methoden worden gebruikt voor toegang tot verzamelingsnavigatie. Voorbeeld:

CollectionEntry<Blog, Post> collectionEntry1 = context.Entry(blog).Collection(e => e.Posts);
CollectionEntry<Blog, Post> collectionEntry2 = context.Entry(blog).Collection<Post>("Posts");
CollectionEntry collectionEntry3 = context.Entry(blog).Collection("Posts");

Sommige bewerkingen zijn gebruikelijk voor alle navigaties. Deze zijn toegankelijk voor zowel referentie- als verzamelingsnavigatie met behulp van de EntityEntry.Navigation methode. Houd er rekening mee dat alleen niet-algemene toegang beschikbaar is wanneer u alle navigaties tegelijk opent. Voorbeeld:

NavigationEntry navigationEntry = context.Entry(blog).Navigation("Posts");

De volgende tabel bevat een overzicht van manieren om te gebruiken ReferenceEntry<TEntity,TProperty>, CollectionEntry<TEntity,TRelatedEntity>en NavigationEntry:

NavigationEntry-lid Beschrijving
MemberEntry.CurrentValue Hiermee haalt u de huidige waarde van de navigatie op en stelt u deze in. Dit is de volledige verzameling voor verzamelingnavigatie.
NavigationEntry.Metadata INavigationBase metagegevens voor de navigatie.
NavigationEntry.IsLoaded Hiermee wordt een waarde opgehaald of ingesteld die aangeeft of de gerelateerde entiteit of verzameling volledig is geladen vanuit de database.
NavigationEntry.Load() Laadt de gerelateerde entiteit of verzameling uit de database; zie Expliciet laden van gerelateerde gegevens.
NavigationEntry.Query() De query die EF Core gebruikt om deze navigatie te laden als een IQueryable die verder kan worden samengesteld. Zie Expliciet laden van gerelateerde gegevens.

Werken met alle eigenschappen van een entiteit

EntityEntry.Properties retourneert een IEnumerable<T> van PropertyEntry voor elke eigenschap van de entiteit. Dit kan worden gebruikt om een actie uit te voeren voor elke eigenschap van de entiteit. Als u bijvoorbeeld een datum/tijd-eigenschap wilt instellen op DateTime.Now:

foreach (var propertyEntry in context.Entry(blog).Properties)
{
    if (propertyEntry.Metadata.ClrType == typeof(DateTime))
    {
        propertyEntry.CurrentValue = DateTime.Now;
    }
}

Daarnaast bevat EntityEntry verschillende methoden om alle eigenschapswaarden tegelijk op te halen en in te stellen. Deze methoden gebruiken de PropertyValues klasse, die een verzameling eigenschappen en hun waarden vertegenwoordigt. PropertyValues kunnen worden verkregen voor huidige of oorspronkelijke waarden of voor de waarden die momenteel zijn opgeslagen in de database. Voorbeeld:

var currentValues = context.Entry(blog).CurrentValues;
var originalValues = context.Entry(blog).OriginalValues;
var databaseValues = await context.Entry(blog).GetDatabaseValuesAsync();

Deze PropertyValues-objecten zijn niet erg nuttig op zichzelf. Ze kunnen echter worden gecombineerd om algemene bewerkingen uit te voeren die nodig zijn bij het bewerken van entiteiten. Dit is handig bij het werken met objecten voor gegevensoverdracht en bij het oplossen van optimistische gelijktijdigheidsconflicten. In de volgende secties ziet u enkele voorbeelden.

Huidige of oorspronkelijke waarden van een entiteit of DTO instellen

De huidige of oorspronkelijke waarden van een entiteit kunnen worden bijgewerkt door waarden uit een ander object te kopiëren. Denk bijvoorbeeld aan een BlogDto DTO (Data Transfer Object) met dezelfde eigenschappen als het entiteitstype:

public class BlogDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Dit kan worden gebruikt om de huidige waarden van een bijgehouden entiteit in te stellen met behulp van PropertyValues.SetValues:

var blogDto = new BlogDto { Id = 1, Name = "1unicorn2" };

context.Entry(blog).CurrentValues.SetValues(blogDto);

Deze techniek wordt soms gebruikt bij het bijwerken van een entiteit met waarden die zijn verkregen uit een serviceaanroep of een client in een toepassing met een n-laag. Houd er rekening mee dat het gebruikte object niet van hetzelfde type hoeft te zijn als de entiteit, zolang het eigenschappen heeft waarvan de namen overeenkomen met die van de entiteit. In het bovenstaande voorbeeld wordt een exemplaar van de DTO BlogDto gebruikt om de huidige waarden van een bijgehouden Blog entiteit in te stellen.

Houd er rekening mee dat eigenschappen alleen worden gemarkeerd als gewijzigd als de waardeset verschilt van de huidige waarde.

Huidige of oorspronkelijke waarden instellen vanuit een woordenlijst

In het vorige voorbeeld worden waarden van een entiteit of DTO-exemplaar ingesteld. Hetzelfde gedrag is beschikbaar wanneer eigenschapswaarden worden opgeslagen als naam-/waardeparen in een woordenlijst. Voorbeeld:

var blogDictionary = new Dictionary<string, object> { ["Id"] = 1, ["Name"] = "1unicorn2" };

context.Entry(blog).CurrentValues.SetValues(blogDictionary);

Huidige of oorspronkelijke waarden instellen vanuit de database

De huidige of oorspronkelijke waarden van een entiteit kunnen worden bijgewerkt met de meest recente waarden uit de database door het aanroepen GetDatabaseValues() of GetDatabaseValuesAsync en gebruiken van het geretourneerde object om huidige of oorspronkelijke waarden in te stellen, of beide. Voorbeeld:

var databaseValues = await context.Entry(blog).GetDatabaseValuesAsync();
context.Entry(blog).CurrentValues.SetValues(databaseValues);
context.Entry(blog).OriginalValues.SetValues(databaseValues);

Een gekloond object maken met huidige, oorspronkelijke of databasewaarden

Het object PropertyValues dat wordt geretourneerd door CurrentValues, OriginalValues of GetDatabaseValues, kan worden gebruikt om een kloon van de entiteit te maken met behulp van PropertyValues.ToObject(). Voorbeeld:

var clonedBlog = (await context.Entry(blog).GetDatabaseValuesAsync()).ToObject();

Houd er rekening mee dat ToObject een nieuw exemplaar wordt geretourneerd dat niet wordt bijgehouden door DbContext. Het geretourneerde object heeft ook geen relaties ingesteld op andere entiteiten.

Het gekloonde object kan handig zijn voor het oplossen van problemen met betrekking tot gelijktijdige updates van de database, met name wanneer gegevensbindingen met objecten van een bepaald type zijn. Zie optimistische gelijktijdigheid voor meer informatie.

Werken met alle navigaties van een entiteit

EntityEntry.Navigations retourneert een IEnumerable<T> van NavigationEntry bij elke navigatie van de entiteit. EntityEntry.References en EntityEntry.Collections doen hetzelfde, maar beperkt tot respectievelijk referentie- of verzamelingsnavigatie. Dit kan worden gebruikt om een actie uit te voeren voor elke navigatie van de entiteit. Bijvoorbeeld om het laden van alle gerelateerde entiteiten af te dwingen:

foreach (var navigationEntry in context.Entry(blog).Navigations)
{
    navigationEntry.Load();
}

Werken met alle leden van een entiteit

Reguliere eigenschappen en navigatie-eigenschappen hebben verschillende status en gedrag. Het is daarom gebruikelijk om navigatie en niet-navigatie afzonderlijk te verwerken, zoals wordt weergegeven in de bovenstaande secties. Soms kan het echter nuttig zijn om iets te doen met om het even welk lid van de entiteit, ongeacht of het een gewone eigenschap of een navigatie-eigenschap is. EntityEntry.Member en EntityEntry.Members worden voor dit doel verstrekt. Voorbeeld:

foreach (var memberEntry in context.Entry(blog).Members)
{
    Console.WriteLine(
        $"Member {memberEntry.Metadata.Name} is of type {memberEntry.Metadata.ClrType.ShortDisplayName()} and has value {memberEntry.CurrentValue}");
}

Als u deze code uitvoert op een blog uit het voorbeeld, wordt de volgende uitvoer gegenereerd:

Member Id is of type int and has value 1
Member Name is of type string and has value .NET Blog
Member Posts is of type IList<Post> and has value System.Collections.Generic.List`1[Post]

Aanbeveling

In de foutopsporingsweergave voor wijzigingentrackers ziet u informatie zoals deze. De foutopsporingsweergave voor de hele wijzigingentracker wordt gegenereerd op basis van de individuele EntityEntry.DebugView van elke bijgehouden entiteit.

Zoeken en FindAsync

DbContext.Find, , DbContext.FindAsyncDbSet<TEntity>.Finden DbSet<TEntity>.FindAsync zijn ontworpen voor een efficiënte lookup van één entiteit wanneer de primaire sleutel bekend is. Controleer eerst of de entiteit al wordt gevolgd en retourneer de entiteit direct als dit het geval is. Er wordt alleen een databasequery uitgevoerd als de entiteit niet lokaal wordt bijgehouden. Denk bijvoorbeeld aan deze code die Zoeken twee keer aanroept voor dezelfde entiteit:

using var context = new BlogsContext();

Console.WriteLine("First call to Find...");
var blog1 = await context.Blogs.FindAsync(1);

Console.WriteLine($"...found blog {blog1.Name}");

Console.WriteLine();
Console.WriteLine("Second call to Find...");
var blog2 = await context.Blogs.FindAsync(1);
Debug.Assert(blog1 == blog2);

Console.WriteLine("...returned the same instance without executing a query.");

De uitvoer van deze code (inclusief EF Core-logboekregistratie) bij het gebruik van SQLite is:

First call to Find...
info: 12/29/2020 07:45:53.682 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (1ms) [Parameters=[@__p_0='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
      SELECT "b"."Id", "b"."Name"
      FROM "Blogs" AS "b"
      WHERE "b"."Id" = @__p_0
      LIMIT 1
...found blog .NET Blog

Second call to Find...
...returned the same instance without executing a query.

U ziet dat de eerste aanroep de entiteit niet lokaal vindt en dus een databasequery uitvoert. Omgekeerd retourneert de tweede aanroep hetzelfde exemplaar zonder query's uit te voeren op de database omdat deze al wordt bijgehouden.

Zoeken retourneert null als een entiteit met de opgegeven sleutel niet lokaal wordt bijgehouden en niet bestaat in de database.

Samengestelde sleutels

Zoeken kan ook worden gebruikt met samengestelde sleutels. Denk bijvoorbeeld aan een OrderLine entiteit met een samengestelde sleutel die bestaat uit de order-id en de product-id:

public class OrderLine
{
    public int OrderId { get; set; }
    public int ProductId { get; set; }

    //...
}

De samengestelde sleutel moet worden geconfigureerd DbContext.OnModelCreating om de belangrijkste onderdelen en hun volgorde te definiëren. Voorbeeld:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<OrderLine>()
        .HasKey(e => new { e.OrderId, e.ProductId });
}

U ziet dat OrderId het eerste deel van de sleutel is en ProductId het tweede deel van de sleutel is. Deze volgorde moet worden gebruikt bij het doorgeven van sleutelwaarden aan Zoeken. Voorbeeld:

var orderline = await context.OrderLines.FindAsync(orderId, productId);

ChangeTracker.Entries gebruiken om toegang te krijgen tot alle bijgehouden entiteiten

Tot nu toe hebben we slechts één EntityEntry tegelijk geopend. ChangeTracker.Entries() retourneert een EntityEntry voor elke entiteit die momenteel wordt bijgehouden door DbContext. Voorbeeld:

using var context = new BlogsContext();
var blogs = await context.Blogs.Include(e => e.Posts).ToListAsync();

foreach (var entityEntry in context.ChangeTracker.Entries())
{
    Console.WriteLine($"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property("Id").CurrentValue}");
}

Met deze code wordt de volgende uitvoer gegenereerd:

Found Blog entity with ID 1
Found Post entity with ID 1
Found Post entity with ID 2

U ziet dat vermeldingen voor zowel blogs als berichten worden geretourneerd. De resultaten kunnen in plaats daarvan worden gefilterd op een specifiek entiteitstype met behulp van de ChangeTracker.Entries<TEntity>() algemene overbelasting:

foreach (var entityEntry in context.ChangeTracker.Entries<Post>())
{
    Console.WriteLine(
        $"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property(e => e.Id).CurrentValue}");
}

In de uitvoer van deze code ziet u dat alleen berichten worden geretourneerd:

Found Post entity with ID 1
Found Post entity with ID 2

Bovendien retourneert het gebruik van de algemene overload algemene EntityEntry<TEntity> exemplaren. Dit is wat in dit voorbeeld vloeiende toegang tot de Id eigenschap toestaat.

Het algemene type dat wordt gebruikt voor filteren hoeft geen toegewezen entiteitstype te zijn; in plaats daarvan kan een niet-toegewezen basistype of -interface worden gebruikt. Als bijvoorbeeld alle entiteitstypen in het model een interface implementeren die de sleuteleigenschap definieert:

public interface IEntityWithKey
{
    int Id { get; set; }
}

Dan kan deze interface worden gebruikt om met de sleutel van elke bijgehouden entiteit in een sterk getypeerde wijze te werken. Voorbeeld:

foreach (var entityEntry in context.ChangeTracker.Entries<IEntityWithKey>())
{
    Console.WriteLine(
        $"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property(e => e.Id).CurrentValue}");
}

DbSet.Local gebruiken om bijgehouden entiteiten op te vragen

EF Core-query's worden altijd uitgevoerd op de database en retourneren alleen entiteiten die zijn opgeslagen in de database. DbSet<TEntity>.Local biedt een mechanisme voor het uitvoeren van query's op dbContext voor lokale, bijgehouden entiteiten.

Omdat DbSet.Local wordt gebruikt om bijgehouden entiteiten op te vragen, is het gebruikelijk om entiteiten in dbContext te laden en vervolgens te werken met die geladen entiteiten. Dit geldt met name voor gegevensbinding, maar kan ook nuttig zijn in andere situaties. In de volgende code wordt de database bijvoorbeeld eerst opgevraagd voor alle blogs en berichten. De Load extensiemethode wordt gebruikt om deze query uit te voeren met de resultaten die door de context worden bijgehouden zonder rechtstreeks naar de toepassing te worden geretourneerd. (Het gebruik ToList of soortgelijke heeft hetzelfde effect, maar met de overhead van het maken van de geretourneerde lijst, die hier niet nodig is.) Het voorbeeld gebruikt DbSet.Local vervolgens om toegang te krijgen tot de lokaal bijgehouden entiteiten:

using var context = new BlogsContext();

await context.Blogs.Include(e => e.Posts).LoadAsync();

foreach (var blog in context.Blogs.Local)
{
    Console.WriteLine($"Blog: {blog.Name}");
}

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"Post: {post.Title}");
}

U ziet dat, in tegenstelling tot ChangeTracker.Entries(), DbSet.Local entiteitsexemplaren rechtstreeks retourneert. Een EntityEntry kan natuurlijk altijd worden verkregen voor de geretourneerde entiteit door aan te roepen DbContext.Entry.

De lokale weergave

DbSet<TEntity>.Local retourneert een weergave van lokaal bijgehouden entiteiten die de huidige EntityState van deze entiteiten weerspiegelen. Dit betekent met name dat:

  • Added Entiteiten zijn opgenomen. Houd er rekening mee dat dit niet het geval is voor normale EF Core-query's, omdat Added entiteiten nog niet bestaan in de database en daarom nooit worden geretourneerd door een databasequery.
  • Deleted entiteiten worden uitgesloten. Houd er rekening mee dat dit opnieuw niet het geval is voor normale EF Core-query's, omdat Deleted entiteiten nog steeds in de database aanwezig zijn en dus worden geretourneerd door databasequery's.

Dit betekent dat DbSet.Local dit een weergave is van de gegevens die de huidige conceptuele status van de entiteitsgrafiek weerspiegelen, met Added entiteiten die zijn opgenomen en Deleted uitgesloten entiteiten. Dit komt overeen met de status van de database nadat SaveChanges is aangeroepen.

Dit is doorgaans de ideale weergave voor gegevensbinding, omdat deze de gegevens aan de gebruiker presenteert op basis van de wijzigingen die door de toepassing zijn aangebracht.

In de volgende code wordt dit gedemonstreerd door één bericht te markeren als Deleted en vervolgens een nieuw bericht toe te voegen, dat als Added wordt gemarkeerd.

using var context = new BlogsContext();

var posts = await context.Posts.Include(e => e.Blog).ToListAsync();

Console.WriteLine("Local view after loading posts:");

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"  Post: {post.Title}");
}

context.Remove(posts[1]);

context.Add(
    new Post
    {
        Title = "What’s next for System.Text.Json?",
        Content = ".NET 5.0 was released recently and has come with many...",
        Blog = posts[0].Blog
    });

Console.WriteLine("Local view after adding and deleting posts:");

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"  Post: {post.Title}");
}

De uitvoer van deze code is:

Local view after loading posts:
  Post: Announcing the Release of EF Core 5.0
  Post: Announcing F# 5
  Post: Announcing .NET 5.0
Local view after adding and deleting posts:
  Post: What’s next for System.Text.Json?
  Post: Announcing the Release of EF Core 5.0
  Post: Announcing .NET 5.0

U ziet dat het verwijderde bericht wordt verwijderd uit de lokale weergave en dat het toegevoegde bericht is opgenomen.

Lokaal gebruiken om entiteiten toe te voegen en te verwijderen

DbSet<TEntity>.Local retourneert een exemplaar van LocalView<TEntity>. Dit is een implementatie van ICollection<T> die meldingen genereert en beantwoordt wanneer entiteiten worden toegevoegd en verwijderd uit de verzameling. (Dit is hetzelfde concept als ObservableCollection<T>, maar geïmplementeerd als een projectie ten opzichte van bestaande EF Core-vermeldingen voor wijzigingen bijhouden, in plaats van als een onafhankelijke verzameling.)

De meldingen van de lokale weergave worden gekoppeld aan het bijhouden van wijzigingen in DbContext, zodat de lokale weergave gesynchroniseerd blijft met dbContext. Specifiek:

  • Het toevoegen van een nieuwe entiteit zorgt DbSet.Local ervoor dat deze wordt bijgehouden door dbContext, meestal in de Added status. (Als de entiteit al een gegenereerde sleutelwaarde heeft, wordt deze bijgehouden zoals Unchanged in plaats daarvan.)
  • Het verwijderen van een entiteit van DbSet.Local zorgt ervoor dat deze wordt gemarkeerd als Deleted.
  • Een entiteit die wordt bijgehouden door DbContext, wordt automatisch weergegeven in de DbSet.Local verzameling. Als u bijvoorbeeld een query uitvoert om meer entiteiten in te voeren, wordt de lokale weergave automatisch bijgewerkt.
  • Een entiteit die is gemarkeerd als Deleted wordt automatisch verwijderd uit de lokale verzameling.

Dit betekent dat de lokale weergave kan worden gebruikt om bijgehouden entiteiten te bewerken door de verzameling toe te voegen en te verwijderen. Laten we bijvoorbeeld de vorige voorbeeldcode wijzigen om berichten toe te voegen aan en te verwijderen uit de lokale verzameling:

using var context = new BlogsContext();

var posts = await context.Posts.Include(e => e.Blog).ToListAsync();

Console.WriteLine("Local view after loading posts:");

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"  Post: {post.Title}");
}

context.Posts.Local.Remove(posts[1]);

context.Posts.Local.Add(
    new Post
    {
        Title = "What’s next for System.Text.Json?",
        Content = ".NET 5.0 was released recently and has come with many...",
        Blog = posts[0].Blog
    });

Console.WriteLine("Local view after adding and deleting posts:");

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"  Post: {post.Title}");
}

De uitvoer blijft ongewijzigd ten opzichte van het vorige voorbeeld omdat wijzigingen in de lokale weergave worden gesynchroniseerd met dbContext.

De lokale weergave gebruiken voor Windows Forms of WPF-gegevensbinding

DbSet<TEntity>.Local vormt de basis voor gegevensbinding met EF Core-entiteiten. Windows Forms en WPF werken echter het beste wanneer ze worden gebruikt met het specifieke type verzameling dat ze verwachten. De lokale weergave ondersteunt het maken van deze specifieke verzamelingstypen:

Voorbeeld:

ObservableCollection<Post> observableCollection = context.Posts.Local.ToObservableCollection();
BindingList<Post> bindingList = context.Posts.Local.ToBindingList();

Zie Aan de slag met WPF voor meer informatie over WPF-gegevensbinding met EF Core en Aan de slag met Windows Forms voor meer informatie over Windows Forms-gegevensbinding met EF Core.

Aanbeveling

De lokale weergave voor een bepaald DbSet-exemplaar wordt pas aangemaakt als dat nodig is wanneer deze voor het eerst wordt geopend en vervolgens in de cache opgeslagen. Het maken van LocalView zelf is snel en er wordt geen aanzienlijk geheugen gebruikt. Het roept echter wel DetectChanges aan, wat traag kan zijn voor grote aantallen entiteiten. De verzamelingen die zijn gemaakt door ToObservableCollection en ToBindingList worden ook op een luie manier gemaakt en vervolgens in de cache opgeslagen. Beide methoden maken nieuwe verzamelingen, die traag kunnen zijn en veel geheugen kunnen gebruiken wanneer er duizenden entiteiten bij betrokken zijn.