Zusätzliche Änderungsnachverfolgung FeaturesAdditional Change Tracking Features

Dieses Dokument behandelt verschiedene Features und Szenarien im Zusammenhang mit der Änderungs Nachverfolgung.This document covers miscellaneous features and scenarios involving change tracking.

Tipp

In diesem Dokument wird davon ausgegangen, dass die Entitäts Zustände und die Grundlagen EF Core Änderungs Nachverfolgung verstanden werden.This document assumes that entity states and the basics of EF Core change tracking are understood. Weitere Informationen zu diesen Themen finden Sie unter Änderungsnachverfolgung in EF Core .See Change Tracking in EF Core for more information on these topics.

Tipp

Sie können den gesamten Code in dieser Dokumentation ausführen und debuggen, indem Sie den Beispielcode von GitHub herunterladen.You can run and debug into all the code in this document by downloading the sample code from GitHub.

Add im Vergleich mit AddAsyncAdd versus AddAsync

Entity Framework Core (EF Core) stellt asynchrone Methoden bereit, wenn die Verwendung dieser Methode eine Daten Bank Interaktion verursachen kann.Entity Framework Core (EF Core) provides async methods whenever using that method may result in a database interaction. Synchrone Methoden werden ebenfalls bereitgestellt, um den mehr Aufwand bei der Verwendung von Datenbanken zu vermeiden, die keinen asynchronen Hochleistungs Zugriff unterstützenSynchronous methods are also provided to avoid overhead when using databases that do not support high performance asynchronous access.

DbContext.Add und DbSet<TEntity>.Add greifen in der Regel nicht auf die Datenbank zu, da diese Methoden grundsätzlich nur die Nachverfolgung von Entitäten starten.DbContext.Add and DbSet<TEntity>.Add do not normally access the database, since these methods inherently just start tracking entities. Einige Formen der Wert Generierung können jedoch auf die Datenbank zugreifen, um einen Schlüsselwert zu generieren.However, some forms of value generation may access the database in order to generate a key value. Der einzige Wert Generator, der diese übernimmt und mit EF Core ausgeliefert wird, ist HiLoValueGenerator<TValue> .The only value generator that does this and ships with EF Core is HiLoValueGenerator<TValue>. Die Verwendung dieses Generators ist nicht üblich. Er wird nie standardmäßig konfiguriert.Using this generator is uncommon; it is never configured by default. Dies bedeutet, dass die große Mehrheit der Anwendungen verwenden sollte Add und nicht AddAsync .This means that the vast majority of applications should use Add, and not AddAsync.

Andere ähnliche Methoden wie Update , Attach und Remove verfügen nicht über über Ladungen, da Sie nie neue Schlüsselwerte generieren und daher nie auf die Datenbank zugreifen müssen.Other similar methods like Update, Attach, and Remove do not have async overloads because they never generate new key values, and hence never need to access the database.

AddRange, UpdateRange, AttachRange und RemoveRangeAddRange, UpdateRange, AttachRange, and RemoveRange

DbSet<TEntity> und DbContext stellen Alternative Versionen von Add , Update , Attach und bereit Remove , die mehrere Instanzen in einem einzelnen-Befehl akzeptieren.DbSet<TEntity> and DbContext provide alternate versions of Add, Update, Attach, and Remove that accept multiple instances in a single call. Diese Methoden sind AddRange , UpdateRange , AttachRange RemoveRange bzw..These methods are AddRange, UpdateRange, AttachRange, and RemoveRange respectively.

Diese Methoden werden als praktische Hilfe bereitgestellt.These methods are provided as a convenience. Die Verwendung einer "Range"-Methode hat die gleiche Funktionalität wie mehrere Aufrufe der äquivalenten Non-Range-Methode.Using a "range" method has the same functionality as multiple calls to the equivalent non-range method. Es gibt keinen signifikanten Leistungsunterschied zwischen den beiden Ansätzen.There is no significant performance difference between the two approaches.

Hinweis

Dies unterscheidet sich von EF6, wobei AddRange und Add sowohl automatisch aufgerufen DetectChanges werden, aber mehrmals aufgerufen wurde, Add dass DetectChanges mehrmals anstatt einmal aufgerufen wird.This is different from EF6, where AddRange and Add both automatically called DetectChanges, but calling Add multiple times caused DetectChanges to be called multiple times instead of once. Dies wurde AddRange in EF6 effizienter.This made AddRange more efficient in EF6. In EF Core wird keine dieser Methoden automatisch aufgerufen DetectChanges .In EF Core, neither of these methods automatically call DetectChanges.

Dbcontext-und dbset-MethodenDbContext versus DbSet methods

Viele Methoden, einschließlich Add , Update , Attach und Remove , verfügen über Implementierungen sowohl für DbSet<TEntity> als auch für DbContext .Many methods, including Add, Update, Attach, and Remove, have implementations on both DbSet<TEntity> and DbContext. Diese Methoden weisen genau das gleiche Verhalten für normale Entitäts Typen auf.These methods have exactly the same behavior for normal entity types. Dies liegt daran, dass der CLR-Typ der Entität nur einem Entitätstyp im EF Core Modell zugeordnet ist.This is because the CLR type of the entity is mapped onto one and only one entity type in the EF Core model. Der CLR-Typ definiert daher vollständig, wo die Entität in das Modell passt, sodass das zu verwendende dbset implizit bestimmt werden kann.Therefore, the CLR type fully defines where the entity fits in the model, and so the DbSet to use can be determined implicitly.

Eine Ausnahme von dieser Regel ist die Verwendung von Entitäts Typen vom Typ "freigegeben", die in EF Core 5,0 eingeführt wurden, hauptsächlich für m:n-joinentitäten.The exception to this rule is when using shared-type entity types, which were introduced in EF Core 5.0, primarily for many-to-many join entities. Wenn Sie einen Entitätstyp vom Typ "Shared" verwenden, muss zunächst ein dbset für den verwendeten EF Core Modelltyp erstellt werden.When using a shared-type entity type, a DbSet must first be created for the EF Core model type that is being used. Methoden wie Add , Update , Attach und Remove können dann für das dbset ohne Mehrdeutigkeit verwendet werden, wenn EF Core Modelltyp verwendet wird.Methods like Add, Update, Attach, and Remove can then be used on the DbSet without any ambiguity as to which EF Core model type is being used.

Entitäts Typen mit gemeinsam genutzten Typen werden standardmäßig für die joinentitäten in m:n-Beziehungen verwendet.Shared-type entity types are used by default for the join entities in many-to-many relationships. Ein Entitätstyp vom Typ "freigegeben" kann auch explizit für die Verwendung in einer m:n-Beziehung konfiguriert werden.A shared-type entity type can also be explicitly configured for use in a many-to-many relationship. Der folgende Code konfiguriert z. b. Dictionary<string, int> als joinentitätstyp:For example, the code below configures Dictionary<string, int> as a join entity type:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .SharedTypeEntity<Dictionary<string, int>>(
            "PostTag",
            b =>
            {
                b.IndexerProperty<int>("TagId");
                b.IndexerProperty<int>("PostId");
            });

    modelBuilder.Entity<Post>()
        .HasMany(p => p.Tags)
        .WithMany(p => p.Posts)
        .UsingEntity<Dictionary<string, int>>(
            "PostTag",
            j => j.HasOne<Tag>().WithMany(),
            j => j.HasOne<Post>().WithMany());
}

Ändern von Fremdschlüsseln und Navigationen zeigt, wie zwei Entitäten durch Nachverfolgen einer neuen Join-Entitäts Instanz zugeordnet werden.Changing Foreign Keys and Navigations shows how to associate two entities by tracking a new join entity instance. Der folgende Code führt dies für den Dictionary<string, int> Entitätstyp "Shared" aus, der für die joinentität verwendet wird:The code below does this for the Dictionary<string, int> shared-type entity type used for the join entity:

using var context = new BlogsContext();

var post = context.Posts.Single(e => e.Id == 3);
var tag = context.Tags.Single(e => e.Id == 1);

var joinEntitySet = context.Set<Dictionary<string, int>>("PostTag");
var joinEntity = new Dictionary<string, int> { ["PostId"] = post.Id, ["TagId"] = tag.Id };
joinEntitySet.Add(joinEntity);

Console.WriteLine(context.ChangeTracker.DebugView.LongView);

context.SaveChanges();

Beachten Sie, dass DbContext.Set<TEntity>(String) verwendet wird, um ein dbset für den PostTag Entitätstyp zu erstellen.Notice that DbContext.Set<TEntity>(String) is used to create a DbSet for the PostTag entity type. Dieses dbset kann dann verwendet werden, um Add mit der neuen Join-Entitäts Instanz aufzurufen.This DbSet can then be used to call Add with the new join entity instance.

Wichtig

Der CLR-Typ, der für joinentitäts Typen gemäß der Konvention verwendet wird, kann sich in zukünftigen Versionen ändern, um die LeistungThe CLR type used for join entity types by convention may change in future releases to improve performance. Richten Sie sich nicht nach einem bestimmten joinentitätstyp, es sei denn, er wurde explizit so konfiguriert, wie dies für Dictionary<string, int> im obigen Code erfolgt ist.Do not depend on any specific join entity type unless it has been explicitly configured as is done for Dictionary<string, int> in the code above.

Eigenschaften-und Feld ZugriffProperty versus field access

Ab EF Core 3,0 verwendet der Zugriff auf Entitäts Eigenschaften standardmäßig das Unterstützungs Feld der-Eigenschaft.Starting with EF Core 3.0, access to entity properties uses the backing field of the property by default. Dies ist effizient und vermeidet das Auslösen von Nebeneffekten aus dem Aufruf von Eigenschaften-Getter und-Setter.This is efficient and avoids triggering side effects from calling property getters and setters. So kann z. b. das verzögerte Laden vermeiden, dass unendliche Schleifen ausgelöst werden.For example, this is how lazy-loading is able to avoid triggering infinite loops. Weitere Informationen zum Konfigurieren von Unterstützungs Feldern im Modell finden Sie unter dahinter liegende Felder .See Backing Fields for more information on configuring backing fields in the model.

Manchmal kann es wünschenswert sein, dass EF Core Nebeneffekte generiert, wenn Sie Eigenschaftswerte ändert.Sometimes it may be desirable for EF Core to generate side-effects when it modifies property values. Wenn z. b. beim Binden von Daten an Entitäten eine Eigenschaft festgelegt wird, werden möglicherweise Benachrichtigungen an U.I.For example, when data binding to entities, setting a property may generate notifications to the U.I. Dies geschieht nicht, wenn das Feld direkt festgelegt wird.which do not happen when setting the field directly. Dies kann durch Ändern von für erreicht werden PropertyAccessMode :This can be achieved by changing the PropertyAccessMode for:

Eigenschafts Zugriffs Modi Field und PreferField bewirken, dass EF Core über das dahinter liegende Feld auf den Eigenschafts Wert zugreift.Property access modes Field and PreferField will cause EF Core to access the property value through its backing field. Ebenso Property bewirken und PreferProperty EF Core auf den Eigenschafts Wert über seinen Getter und Setter zugreifen.Likewise, Property and PreferProperty will cause EF Core to access the property value through its getter and setter.

Wenn Field oder Property verwendet werden und EF Core nicht auf den Wert über das Feld bzw. die Eigenschaften Getter/Setter zugreifen können, löst EF Core eine Ausnahme aus.If Field or Property are used and EF Core cannot access the value through the field or property getter/setter respectively, then EF Core will throw an exception. Dadurch wird sichergestellt, dass EF Core immer den Feld-/Eigenschaftenzugriff verwendet, wenn Sie den Eindruck habenThis ensures EF Core is always using field/property access when you think it is.

Auf der anderen Seite greifen die PreferField Modi und auf PreferProperty die Verwendung der Eigenschaft bzw. des dahinter liegenden Felds zurück, wenn es nicht möglich ist, den bevorzugten Zugriff zu verwenden.On the other hand, the PreferField and PreferProperty modes will fall back to using the property or backing field respectively if it is not possible to use the preferred access. PreferField ist der Standardwert von EF Core 3,0 ab.PreferField is the default from EF Core 3.0 onwards. Dies bedeutet, dass EF Core immer dann Felder verwendet, wenn es nicht möglich ist, wenn auf eine Eigenschaft über den zugehörigen Getter oder Setter zugegriffen werden muss.This means EF Core will use fields whenever it can, but will not fail if a property must be accessed through its getter or setter instead.

FieldDuringConstruction und PreferFieldDuringConstruction Konfigurieren EF Core nur beim Erstellen von Entitäts Instanzen für die Verwendung von Unterstützungs Feldern.FieldDuringConstruction and PreferFieldDuringConstruction configure EF Core to use of backing fields only when creating entity instances. Auf diese Weise können Abfragen ohne Getter-und Setter-Nebeneffekte ausgeführt werden, während spätere Eigenschafts Änderungen durch EF Core diese Nebeneffekte verursachen.This allows queries to be executed without getter and setter side effects, while later property changes by EF Core will cause these side effects. PreferFieldDuringConstruction war der Standardwert vor EF Core 3,0.PreferFieldDuringConstruction was the default prior to EF Core 3.0.

Die verschiedenen Eigenschaften Zugriffs Modi werden in der folgenden Tabelle zusammengefasst:The different property access modes are summarized in the following table:

PropertyaccessmodePropertyAccessMode BevorzuPreference Einrichtung von Entitäten bevorzugenPreference creating entities FallbackFallback Fallback beim Erstellen von EntitätenFallback creating entities
Field FeldField FeldField Löst ausThrows Löst ausThrows
Property EigenschaftProperty EigenschaftProperty Löst ausThrows Löst ausThrows
PreferField FeldField FeldField EigenschaftProperty EigenschaftProperty
PreferProperty EigenschaftProperty EigenschaftProperty FeldField FeldField
FieldDuringConstruction EigenschaftProperty FeldField FeldField Löst ausThrows
PreferFieldDuringConstruction EigenschaftProperty FeldField FeldField EigenschaftProperty

Temporäre WerteTemporary values

EF Core erstellt temporäre Schlüsselwerte, wenn neue Entitäten nachverfolgt werden, deren echte Schlüsselwerte von der Datenbank generiert werden, wenn SaveChanges aufgerufen wird.EF Core creates temporary key values when tracking new entities that will have real key values generated by the database when SaveChanges is called. Eine Übersicht über die Verwendung dieser temporären Werte finden Sie unter Änderungsnachverfolgung in EF Core .See Change Tracking in EF Core for an overview of how these temporary values are used.

Zugreifen auf temporäre WerteAccessing temporary values

Ab EF Core 3,0 werden temporäre Werte in der Änderungs Protokollierung gespeichert und nicht direkt auf Entitäts Instanzen festgelegt.Starting with EF Core 3.0, temporary values are stored in the change tracker and not set onto entity instances directly. Diese temporären Werte werden jedoch verfügbar gemacht, wenn die verschiedenen Mechanismen für den Zugriff auf verfolgte Entitätenverwendet werden .However, these temporary values are exposed when using the various mechanisms for Accessing Tracked Entities. Beispielsweise greift der folgende Code mithilfe von auf einen temporären Wert zu EntityEntry.CurrentValues :For example, the following code accesses a temporary value using EntityEntry.CurrentValues:

using var context = new BlogsContext();

var blog = new Blog { Name = ".NET Blog" };

context.Add(blog);

Console.WriteLine($"Blog.Id set on entity is {blog.Id}");
Console.WriteLine($"Blog.Id tracked by EF is {context.Entry(blog).Property(e => e.Id).CurrentValue}");

Die Ausgabe dieses Codes lautet wie folgt:The output from this code is:

Blog.Id set on entity is 0
Blog.Id tracked by EF is -2147482643

PropertyEntry.IsTemporary kann verwendet werden, um nach temporären Werten zu suchen.PropertyEntry.IsTemporary can be used to check for temporary values.

Bearbeiten temporärer WerteManipulating temporary values

Es ist manchmal hilfreich, explizit mit temporären Werten zu arbeiten.It is sometimes useful to explicitly work with temporary values. Beispielsweise kann eine Auflistung von neuen Entitäten auf einem Webclient erstellt und dann zurück zum Server serialisiert werden.For example, a collection of new entities might be created on a web client and then serialized back to the server. Fremdschlüssel Werte sind eine Möglichkeit zum Einrichten von Beziehungen zwischen diesen Entitäten.Foreign key values are one way to set up relationships between these entities. Der folgende Code verwendet diesen Ansatz, um ein Diagramm neuer Entitäten nach Fremdschlüssel zuzuordnen, während gleichzeitig beim Aufrufen von SaveChanges echte Schlüsselwerte generiert werden können.The following code uses this approach to associate a graph of new entities by foreign key, while still allowing real key values to be generated when SaveChanges is called.

var blogs = new List<Blog> { new Blog { Id = -1, Name = ".NET Blog" }, new Blog { Id = -2, Name = "Visual Studio Blog" } };

var posts = new List<Post>
{
    new Post
    {
        Id = -1,
        BlogId = -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,
        BlogId = -2,
        Title = "Disassembly improvements for optimized managed debugging",
        Content = "If you are focused on squeezing out the last bits of performance for your .NET service or..."
    }
};

using var context = new BlogsContext();

foreach (var blog in blogs)
{
    context.Add(blog).Property(e => e.Id).IsTemporary = true;
}

foreach (var post in posts)
{
    context.Add(post).Property(e => e.Id).IsTemporary = true;
}

Console.WriteLine(context.ChangeTracker.DebugView.LongView);

context.SaveChanges();

Console.WriteLine(context.ChangeTracker.DebugView.LongView);

Beachten Sie Folgendes:Notice that:

  • Negative Zahlen werden als temporäre Schlüsselwerte verwendet. Dies ist nicht erforderlich, ist jedoch eine gängige Konvention, um Schlüssel Konflikte zu verhindern.Negative numbers are used as temporary key values; this is not required, but is a common convention to prevent key clashes.
  • Der Post.BlogId Eigenschaft "FK" wird der gleiche negative Wert wie der PK des zugehörigen Blogs zugewiesen.The Post.BlogId FK property is assigned the same negative value as the PK of the associated blog.
  • Die PK-Werte werden als temporär gekennzeichnet, indem Sie festlegen, IsTemporary nachdem jede Entität nachverfolgt wird.The PK values are marked as temporary by setting IsTemporary after each entity is tracked. Dies ist erforderlich, da davon ausgegangen wird, dass jeder von der Anwendung bereitgestellte Schlüsselwert ein echter Schlüsselwert ist.This is necessary because any key value supplied by the application is assumed to be a real key value.

Wenn Sie vor dem Aufrufen von SaveChanges die Debugansicht für die Änderungs Nachverfolgung anzeigen, sehen Sie, dass die PK-Werte als temporär gekennzeichnet sind und Beiträge mit den richtigen Blogs verknüpft sindLooking at the change tracker debug view before calling SaveChanges shows that the PK values are marked as temporary and posts are associated with the correct blogs, including fixup of navigations:

Blog {Id: -2} Added
  Id: -2 PK Temporary
  Name: 'Visual Studio Blog'
  Posts: [{Id: -2}]
Blog {Id: -1} Added
  Id: -1 PK Temporary
  Name: '.NET Blog'
  Posts: [{Id: -1}]
Post {Id: -2} Added
  Id: -2 PK Temporary
  BlogId: -2 FK
  Content: 'If you are focused on squeezing out the last bits of perform...'
  Title: 'Disassembly improvements for optimized managed debugging'
  Blog: {Id: -2}
  Tags: []
Post {Id: -1} Added
  Id: -1 PK Temporary
  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}

Nach SaveChanges dem Aufruf von wurden diese temporären Werte durch von der Datenbank generierte reale Werte ersetzt:After calling SaveChanges, these temporary values have been replaced by real values generated by the database:

Blog {Id: 1} Unchanged
  Id: 1 PK
  Name: '.NET Blog'
  Posts: [{Id: 1}]
Blog {Id: 2} Unchanged
  Id: 2 PK
  Name: 'Visual Studio Blog'
  Posts: [{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}
  Tags: []
Post {Id: 2} Unchanged
  Id: 2 PK
  BlogId: 2 FK
  Content: 'If you are focused on squeezing out the last bits of perform...'
  Title: 'Disassembly improvements for optimized managed debugging'
  Blog: {Id: 2}
  Tags: []

Arbeiten mit StandardwertenWorking with default values

EF Core ermöglicht es einer Eigenschaft, ihren Standardwert aus der Datenbank zu erhalten, wenn SaveChanges aufgerufen wird.EF Core allows a property to get its default value from the database when SaveChanges is called. Wie bei generierten Schlüsselwerten wird EF Core nur einen Standardwert aus der Datenbank verwenden, wenn kein Wert explizit festgelegt wurde.Like with generated key values, EF Core will only use a default from the database if no value has been explicitly set. Sehen Sie sich beispielsweise den folgenden Entitätstyp an:For example, consider the following entity type:

public class Token
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime ValidFrom { get; set; }
}

Die- ValidFrom Eigenschaft ist so konfiguriert, dass Sie einen Standardwert aus der Datenbank erhält:The ValidFrom property is configured to get a default value from the database:

modelBuilder
    .Entity<Token>()
    .Property(e => e.ValidFrom)
    .HasDefaultValueSql("CURRENT_TIMESTAMP");

Wenn eine Entität dieses Typs eingefügt wird, ermöglicht EF Core der Datenbank, den Wert zu generieren, es sei denn, stattdessen wurde ein expliziter Wert festgelegt.When inserting an entity of this type, EF Core will let the database generate the value unless an explicit value has been set instead. Beispiel:For example:

using var context = new BlogsContext();

context.AddRange(
    new Token { Name = "A" },
    new Token { Name = "B", ValidFrom = new DateTime(1111, 11, 11, 11, 11, 11) });

context.SaveChanges();

Console.WriteLine(context.ChangeTracker.DebugView.LongView);

Wenn Sie die Debugansicht der Änderungs Nachverfolgung anzeigen, wird angezeigt, dass das erste Token ValidFrom von der Datenbank generiert wurde, während das zweite Token den explizit festgelegten Wert verwendet hat:Looking at the change tracker debug view shows that the first token had ValidFrom generated by the database, while the second token used the value explicitly set:

Token {Id: 1} Unchanged
  Id: 1 PK
  Name: 'A'
  ValidFrom: '12/30/2020 6:36:06 PM'
Token {Id: 2} Unchanged
  Id: 2 PK
  Name: 'B'
  ValidFrom: '11/11/1111 11:11:11 AM'

Hinweis

Die Verwendung von Daten Bank Standardwerten erfordert, dass für die Daten Bank Spalte eine Standardwert Einschränkung konfiguriert ist.Using database default values requires that the database column has a default value constraint configured. Dies erfolgt automatisch durch EF Core Migrationen, wenn HasDefaultValueSql oder verwendet wird HasDefaultValue .This is done automatically by EF Core migrations when using HasDefaultValueSql or HasDefaultValue. Stellen Sie sicher, dass Sie die DEFAULT-Einschränkung für die Spalte auf andere Weise erstellen, wenn Sie keine EF Core Migrationen verwenden.Make sure to create the default constraint on the column in some other way when not using EF Core migrations.

Verwenden von Werte zulässt-EigenschaftenUsing nullable properties

EF Core kann ermitteln, ob eine Eigenschaft festgelegt wurde, indem Sie den Eigenschafts Wert mit dem CLR-Standardwert für den betreffenden Typ vergleicht.EF Core is able to determine whether or not a property has been set by comparing the property value to the CLR default for the that type. Dies funktioniert in den meisten Fällen gut, bedeutet aber, dass die CLR-Standardeinstellung nicht explizit in die Datenbank eingefügt werden kann.This works well in most cases, but means that the CLR default cannot be explicitly inserted into the database. Stellen Sie sich beispielsweise eine Entität mit einer ganzzahligen Eigenschaft vor:For example, consider an entity with an integer property:

public class Foo1
{
    public int Id { get; set; }
    public int Count { get; set; }
}

Wenn diese Eigenschaft für den Daten Bank Standardwert-1 konfiguriert ist:Where that property is configured to have a database default of -1:

modelBuilder
    .Entity<Foo1>()
    .Property(e => e.Count)
    .HasDefaultValue(-1);

Die Absicht ist, dass der Standardwert-1 verwendet wird, wenn kein expliziter Wert festgelegt ist.The intention is that the default of -1 will be used whenever an explicit value is not set. Das Festlegen des Werts auf 0 (die CLR-Standardeinstellung für ganze Zahlen) kann jedoch nicht unterschieden werden, EF Core kein Wert festgelegt wird. Dies bedeutet, dass es nicht möglich ist, 0 für diese Eigenschaft einzufügen.However, setting the value to 0 (the CLR default for integers) is indistinguishable to EF Core from not setting any value, this means that it is not possible to insert 0 for this property. Beispiel:For example:

using var context = new BlogsContext();

var fooA = new Foo1 { Count = 10 };
var fooB = new Foo1 { Count = 0 };
var fooC = new Foo1();

context.AddRange(fooA, fooB, fooC);
context.SaveChanges();

Debug.Assert(fooA.Count == 10);
Debug.Assert(fooB.Count == -1); // Not what we want!
Debug.Assert(fooC.Count == -1);

Beachten Sie, dass die-Instanz, bei der Count explizit auf 0 festgelegt wurde, immer noch den Standardwert aus der Datenbank erhält. Dies ist nicht beabsichtigt.Notice that the instance where Count was explicitly set to 0 is still gets the default value from the database, which is not what we intended. Eine einfache Möglichkeit, dies zu umgehen, besteht darin, dass die Count Eigenschaft NULL-Werte zulässt:An easy way to deal with this is to make the Count property nullable:

public class Foo2
{
    public int Id { get; set; }
    public int? Count { get; set; }
}

Dadurch wird der CLR-Standardwert NULL (anstelle von 0), d. h., wenn er explizit festgelegt wird, wird 0 eingefügt:This makes the CLR default null, instead of 0, which means 0 will now be inserted when explicitly set:

using var context = new BlogsContext();

var fooA = new Foo2 { Count = 10 };
var fooB = new Foo2 { Count = 0 };
var fooC = new Foo2();

context.AddRange(fooA, fooB, fooC);
context.SaveChanges();

Debug.Assert(fooA.Count == 10);
Debug.Assert(fooB.Count == 0);
Debug.Assert(fooC.Count == -1);

Verwenden von Werte zulässt-Unterstützungs FeldernUsing nullable backing fields

Hinweis

Dieses Unterstützungs Feld Muster, das NULL-Werte zulässt, wird von EF Core 5,0 und höher unterstützt.This nullable backing field pattern is supported by EF Core 5.0 and later.

Das Problem, dass die Eigenschaft auf NULL festgelegt werden kann, kann im Domänen Modell nicht konzeptionell NULL sein.The problem with making the property nullable that it may not be conceptually nullable in the domain model. Wenn Sie erzwingen, dass die Eigenschaft auf NULL festgelegt werden kann, wird das Modell gefährdetForcing the property to be nullable therefore compromises the model.

Ab EF Core 5,0 kann die-Eigenschaft nicht auf NULL festgelegt werden, und nur das Unterstützungs Feld lässt NULL-Werte zu.Starting with EF Core 5.0, the property can be left non-nullable, with only the backing field being nullable. Beispiel:For example:

public class Foo3
{
    public int Id { get; set; }

    private int? _count;

    public int Count
    {
        get => _count ?? -1;
        set => _count = value;
    }
}

Dadurch kann die CLR-Standardeinstellung (0) eingefügt werden, wenn die-Eigenschaft explizit auf 0 festgelegt ist, während die-Eigenschaft im Domänen Modell nicht als Werte zulässt verfügbar gemacht werden muss.This allows the CLR default (0) to be inserted if the property is explicitly set to 0, while not needing to expose the property as nullable in the domain model. Beispiel:For example:

using var context = new BlogsContext();

var fooA = new Foo3 { Count = 10 };
var fooB = new Foo3 { Count = 0 };
var fooC = new Foo3();

context.AddRange(fooA, fooB, fooC);
context.SaveChanges();

Debug.Assert(fooA.Count == 10);
Debug.Assert(fooB.Count == 0);
Debug.Assert(fooC.Count == -1);

Auf NULL festleg Bare Unterstützungs Felder für boolesche EigenschaftenNullable backing fields for bool properties

Dieses Muster ist besonders nützlich bei der Verwendung von booleschen Eigenschaften mit vom Speicher generierten Standardwerten.This pattern is especially useful when using bool properties with store-generated defaults. Da der CLR-Standard für den Wert bool "false" hat, bedeutet dies, dass "false" nicht explizit mit dem normalen Muster eingefügt werden kann.Since the CLR default for bool is "false", it means that "false" cannot be inserted explicitly using the normal pattern. Nehmen Sie beispielsweise einen User Entitätstyp an:For example, consider a User entity type:

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

    private bool? _isAuthorized;

    public bool IsAuthorized
    {
        get => _isAuthorized ?? true;
        set => _isAuthorized = value;
    }
}

Die- IsAuthorized Eigenschaft wird mit dem Standardwert "true" der Datenbank konfiguriert:The IsAuthorized property is configured with a database default value of "true":

modelBuilder
    .Entity<User>()
    .Property(e => e.IsAuthorized)
    .HasDefaultValue(true);

Die IsAuthorized Eigenschaft kann vor dem Einfügen explizit auf "true" oder "false" festgelegt werden, oder die Eigenschaft kann nicht festgelegt werden. in diesem Fall wird der Daten Bank Standard verwendet:The IsAuthorized property can be set to "true" or "false" explicitly before inserting, or can be left unset in which case the database default will be used:

using var context = new BlogsContext();

var userA = new User { Name = "Mac" };
var userB = new User { Name = "Alice", IsAuthorized = true };
var userC = new User { Name = "Baxter", IsAuthorized = false }; // Always deny Baxter access!

context.AddRange(userA, userB, userC);

context.SaveChanges();

Die Ausgabe von SaveChanges bei der Verwendung von SQLite zeigt, dass der Daten Bank Standard für Mac verwendet wird, während für Alice und Baxter explizite Werte festgelegt sind:The output from SaveChanges when using SQLite shows that the database default is used for Mac, while explicit values are set for Alice and Baxter:

-- Executed DbCommand (0ms) [Parameters=[@p0='Mac' (Size = 3)], CommandType='Text', CommandTimeout='30']
INSERT INTO "User" ("Name")
VALUES (@p0);
SELECT "Id", "IsAuthorized"
FROM "User"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

-- Executed DbCommand (0ms) [Parameters=[@p0='True' (DbType = String), @p1='Alice' (Size = 5)], CommandType='Text', CommandTimeout='30']
INSERT INTO "User" ("IsAuthorized", "Name")
VALUES (@p0, @p1);
SELECT "Id"
FROM "User"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

-- Executed DbCommand (0ms) [Parameters=[@p0='False' (DbType = String), @p1='Baxter' (Size = 6)], CommandType='Text', CommandTimeout='30']
INSERT INTO "User" ("IsAuthorized", "Name")
VALUES (@p0, @p1);
SELECT "Id"
FROM "User"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Nur Schema StandardwerteSchema defaults only

In manchen Fällen ist es sinnvoll, die Standardeinstellungen des Datenbankschemas zu verwenden, das durch EF Core Migrationen erstellt wurde, EF Core ohne diese Werte für EinfügungenSometimes it is useful to have defaults in the database schema created by EF Core migrations without EF Core ever using these values for inserts. Dies kann erreicht werden, indem Sie die-Eigenschaft wie folgt konfigurieren PropertyBuilder.ValueGeneratedNever :This can be achieved by configuring the property as PropertyBuilder.ValueGeneratedNever For example:

modelBuilder
    .Entity<Bar>()
    .Property(e => e.Count)
    .HasDefaultValue(-1)
    .ValueGeneratedNever();