Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Es ist möglich, einen Konstruktor mit Parametern zu definieren und EF Core diesen Konstruktor beim Erstellen einer Entitäteninstanz aufzurufen. Die Konstruktorparameter können an zugeordnete Eigenschaften oder an verschiedene Arten von Diensten gebunden werden, um Verhaltensweisen wie Lazy-Loading zu erleichtern.
Hinweis
Derzeit erfolgt die gesamte Konstruktorbindung nach Konventionen. Die Konfiguration bestimmter zu verwendenden Konstruktoren ist für eine zukünftige Version geplant.
Bindung an zugeordnete Eigenschaften
Betrachten Sie ein typisches Blog-/Postmodell:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<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 DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Wenn EF Core Instanzen dieser Typen erstellt, z. B. für die Ergebnisse einer Abfrage, ruft er zuerst den Standardparameterlosen Konstruktor auf und legt dann jede Eigenschaft auf den Wert aus der Datenbank fest. Wenn EF Core jedoch einen parametrisierten Konstruktor mit Parameternamen und Typen findet, die mit denen der zugeordneten Eigenschaften übereinstimmen, ruft er stattdessen den parametrisierten Konstruktor mit Werten für diese Eigenschaften auf und legt nicht explizit jede Eigenschaft fest. Beispiel:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Hinweise, die Sie beachten sollten:
- Nicht alle Eigenschaften müssen über Konstruktorparameter verfügen. Die Post.Content-Eigenschaft wird z. B. nicht von einem Konstruktorparameter festgelegt, sodass EF Core sie nach dem Aufrufen des Konstruktors auf normale Weise festlegen wird.
- Die Typen und Namen der Parameter müssen mit den Typen und Namen der Eigenschaften übereinstimmen, mit der Ausnahme, dass Eigenschaften Pascal-cased sein können, während die Parameter camel-cased sind.
- EF Core kann keine Navigationseigenschaften (z. B. Blog oder Beiträge oben) mithilfe eines Konstruktors festlegen.
- Der Konstruktor kann öffentlich, privat sein oder andere Barrierefreiheit haben. Lazy-Loading-Proxies erfordern jedoch, dass der Konstruktor von der erbenden Proxy-Klasse aus zugänglich ist. In der Regel bedeutet dies, dass sie entweder öffentlich oder geschützt wird.
Schreibgeschützte Eigenschaften
Sobald Eigenschaften über den Konstruktor festgelegt werden, kann es sinnvoll sein, einige davon schreibgeschützt zu machen. EF Core unterstützt dies, aber es gibt einige Dinge, die Sie sich ansehen sollten:
- Eigenschaften ohne Setter werden nicht nach Konvention zugeordnet. (Auf diese Weise werden Eigenschaften zugeordnet, die nicht zugeordnet werden sollten, z. B. berechnete Eigenschaften.)
- Für die Verwendung automatisch generierter Schlüsselwerte ist eine Schlüsseleigenschaft mit Lese-/Schreibzugriff erforderlich, da der Schlüsselwert beim Einfügen neuer Entitäten vom Schlüsselgenerator festgelegt werden muss.
Eine einfache Möglichkeit, diese Dinge zu vermeiden, besteht darin, private Setter zu verwenden. Beispiel:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Author { get; private set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; private set; }
public string Title { get; private set; }
public string Content { get; set; }
public DateTime PostedOn { get; private set; }
public Blog Blog { get; set; }
}
EF Core betrachtet eine Eigenschaft mit einem privaten Setter als Lese-/Schreibzugriff, was bedeutet, dass alle Eigenschaften wie zuvor zugeordnet werden und der Schlüssel dennoch datenbankgeneriert sein kann.
Eine Alternative zur Verwendung privater Setter besteht darin, Eigenschaften wirklich schreibgeschützt zu machen und explizitere Zuordnungen in OnModelCreating hinzuzufügen. Ebenso können einige Eigenschaften vollständig entfernt und nur durch Felder ersetzt werden. Betrachten Sie z. B. diese Entitätstypen:
public class Blog
{
private int _id;
public Blog(string name, string author)
{
Name = name;
Author = author;
}
public string Name { get; }
public string Author { get; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
private int _id;
public Post(string title, DateTime postedOn)
{
Title = title;
PostedOn = postedOn;
}
public string Title { get; }
public string Content { get; set; }
public DateTime PostedOn { get; }
public Blog Blog { get; set; }
}
Und diese Konfiguration in OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Author);
b.Property(e => e.Name);
});
modelBuilder.Entity<Post>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Title);
b.Property(e => e.PostedOn);
});
}
Zu berücksichtigende Punkte:
- Der Schlüssel "Eigenschaft" ist jetzt ein Feld. Es handelt sich nicht um ein
readonlyFeld, sodass vom Speicher generierte Schlüssel verwendet werden können. - Die anderen Eigenschaften sind schreibgeschützte Eigenschaften, die nur im Konstruktor festgelegt werden.
- Wenn der Primärschlüsselwert nur von EF festgelegt oder aus der Datenbank gelesen wird, müssen Sie ihn nicht in den Konstruktor aufnehmen. Dadurch wird die Eigenschaft als einfaches Feld beibehalten und es wird klar gemacht, dass sie beim Erstellen neuer Blogs oder Beiträge nicht explizit festgelegt werden sollte.
Hinweis
Dieser Code führt zu einer Compilerwarnung "169", die angibt, dass das Feld nie verwendet wird. Dies kann ignoriert werden, da in Wirklichkeit EF Core das Feld auf extralinguistische Weise verwendet.
Injizieren von Diensten
EF Core kann auch "Dienste" in den Konstruktor eines Entitätstyps einfügen. Beispielsweise kann Folgendes eingefügt werden:
-
DbContext- die aktuelle Kontextinstanz, die auch als abgeleiteter DbContext-Typ eingegeben werden kann -
ILazyLoader- der Lazy-Loading-Dienst—siehe die Lazy-Loading-Dokumentation für weitere Details -
Action<object, string>- ein Lazy-Loading-Delegate—siehe die Lazy-Loading-Dokumentation für weitere Details -
IEntityType– die EF Core-Metadaten, die diesem Entitätstyp zugeordnet sind
Hinweis
Derzeit können nur dienste, die von EF Core bekannt sind, eingefügt werden. Die Unterstützung für das Einfügen von Anwendungsdiensten wird für eine zukünftige Version berücksichtigt.
Beispielsweise kann ein injizierter DbContext verwendet werden, um selektiv auf die Datenbank zuzugreifen, um Informationen zu verwandten Entitäten abzurufen, ohne sie alle zu laden. Im folgenden Beispiel wird dies verwendet, um die Anzahl der Beiträge in einem Blog zu erhalten, ohne die Beiträge zu laden:
public class Blog
{
public Blog()
{
}
private Blog(BloggingContext context)
{
Context = context;
}
private BloggingContext Context { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; set; }
public int PostsCount
=> Posts?.Count
?? Context?.Set<Post>().Count(p => Id == EF.Property<int?>(p, "BlogId"))
?? 0;
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Ein paar Dinge, die Sie beachten sollten:
- Der Konstruktor ist privat, da er nur von EF Core aufgerufen wird, und es gibt einen anderen öffentlichen Konstruktor für die allgemeine Verwendung.
- Der Code, der den injizierten Dienst verwendet (d. h. den Kontext), ist darauf ausgelegt, defensiv zu agieren, um Fälle zu behandeln, in denen der Kontext
nullist, weil EF Core die Instanz nicht erstellt. - Da der Dienst in einer Lese-/Schreibeigenschaft gespeichert wird, wird er zurückgesetzt, wenn die Entität an eine neue Kontextinstanz angefügt wird.
Warnung
Das Injizieren des DbContext wird oft als Antimuster betrachtet, da er Ihre Entitätstypen direkt mit EF Core koppelt. Berücksichtigen Sie sorgfältig alle Optionen, bevor Sie die Diensteinjektion auf diese Weise verwenden.