Schatten- und Indexereigenschaften

Schatteneigenschaften sind Eigenschaften, die nicht in Ihrer .NET-Entitätsklasse definiert sind, aber für diesen Entitätstyp im EF Core-Modell definiert sind. Der Wert und der Status dieser Eigenschaften werden nur in der Änderungsnachverfolgung verwaltet. Schatteneigenschaften sind nützlich, wenn die Datenbank Daten enthält, die nicht für die zugeordneten Entitätstypen verfügbar gemacht werden sollten.

Indexereigenschaften sind Entitätstypeigenschaften, die von einem Indexer in der .NET-Entitätsklasse unterstützt werden. Auf sie kann mithilfe des Indexers auf den .NET-Klasseninstanzen zugegriffen werden. Außerdem können Sie dem Entitätstyp zusätzliche Eigenschaften hinzufügen, ohne die CLR-Klasse zu ändern.

Fremdschlüssel-Schatteneigenschaften

Schatteneigenschaften werden am häufigsten für Fremdschlüsseleigenschaften verwendet, bei denen sie dem Modell laut Konvention hinzugefügt werden, wenn keine Fremdschlüsseleigenschaft laut Konvention oder explizit konfiguriert gefunden wurde. Die Beziehung wird durch Navigationseigenschaften dargestellt, aber in der Datenbank wird sie durch eine Fremdschlüsseleinschränkung erzwungen, und der Wert für die Fremdschlüsselspalte wird in der entsprechenden Schatteneigenschaft gespeichert.

Die Eigenschaft wird <navigation property name><principal key property name> genannt (die Navigation auf der abhängigen Entität, die auf die Prinzipalentität verweist, wird für die Benennung verwendet). Wenn der Name der Hauptschlüsseleigenschaft mit dem Namen der Navigationseigenschaft beginnt, ist der Name einfach nur <principal key property name>. Wenn keine Navigationseigenschaft für die abhängige Entität vorhanden ist, wird der Prinzipaltypname, der mit dem primären oder alternativen Schlüsseleigenschaftsnamen verkettet ist, an deren Stelle verwendet: <principal type name><principal key property name>.

Die folgende Codeliste führt beispielsweise dazu, dass eine BlogId-Schatteneigenschaft in der Post-Entität eingeführt wird:

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    // Since there is no CLR property which holds the foreign
    // key for this relationship, a shadow property is created.
    public Blog Blog { get; set; }
}

Konfigurieren von Schatteneigenschaften

Sie können die Fluent-API verwenden, um Schatteneigenschaften zu konfigurieren. Nachdem Sie die Zeichenfolgenüberladung von Property<TProperty>(String) aufgerufen haben, können Sie alle Konfigurationsaufrufe verketten, wie Sie es für andere Eigenschaften tun würden. Im folgenden Beispiel wird eine Schatteneigenschaft erstellt, da Blog keine CLR-Eigenschaft mit dem Namen LastUpdated hat:

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property<DateTime>("LastUpdated");
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Wenn der für die Property-Methode bereitgestellte Name mit dem Namen einer vorhandenen Eigenschaft übereinstimmt (einer Schatteneigenschaft oder einer für die Entitätsklasse definierten), konfiguriert der Code diese vorhandene Eigenschaft, anstatt eine neue Schatteneigenschaft einzuführen.

Zugreifen auf Schatteneigenschaften

Schatteneigenschaftsenwerte können über die ChangeTracker-API abgerufen und geändert werden:

context.Entry(myBlog).Property("LastUpdated").CurrentValue = DateTime.Now;

Schatteneigenschaften können in LINQ-Abfragen über die statische EF.Property-Methode referenziert werden:

var blogs = context.Blogs
    .OrderBy(b => EF.Property<DateTime>(b, "LastUpdated"));

Auf Schatteneigenschaften kann nicht nach einer Abfrage ohne Nachverfolgung zugegriffen werden, da die zurückgegebenen Entitäten nicht von der Änderungsverfolgung nachverfolgt werden.

Konfigurieren von Indexereigenschaften

Sie können die Fluent-API verwenden, um Indexereigenschaften zu konfigurieren. Nachdem Sie die Methode IndexerProperty aufgerufen haben, können Sie alle Konfigurationsaufrufe verketten, wie Sie es für andere Eigenschaften tun würden. Im folgenden Beispiel hat Blog einen Indexer definiert und wird zum Erstellen einer Indexereigenschaft verwendet.

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().IndexerProperty<DateTime>("LastUpdated");
    }
}

public class Blog
{
    private readonly Dictionary<string, object> _data = new Dictionary<string, object>();
    public int BlogId { get; set; }

    public object this[string key]
    {
        get => _data[key];
        set => _data[key] = value;
    }
}

Wenn der für die IndexerProperty-Methode bereitgestellte Name mit dem Namen einer vorhandenen Indexereigenschaft übereinstimmt, konfiguriert der Code diese vorhandene Eigenschaft. Wenn der Entitätstyp über eine Eigenschaft verfügt, die von einer Eigenschaft der Entitätsklasse unterstützt wird, wird eine Ausnahme ausgelöst, da auf Indexereigenschaften nur über den Indexer zugegriffen werden darf.

Indexereigenschaften können in LINQ-Abfragen über die statische EF.Property-Methode wie oben dargestellt oder mithilfe der CLR-Indexereigenschaft referenziert werden.

Entitätstypen für Eigenschaftenbehälter

Entitätstypen, die nur Indexereigenschaften enthalten, werden als Entitätstypen für Eigenschaftenbehälter bezeichnet. Diese Entitätstypen verfügen nicht über Schatteneigenschaften, und EF erstellt stattdessen Indexereigenschaften. Derzeit wird nur Dictionary<string, object> als Entitätstyp für Eigenschaftenbehälter unterstützt. Dies muss als gemeinsamer Entitätstyptyp mit einem eindeutigen Namen konfiguriert werden, und die entsprechende DbSet-Eigenschaft muss mithilfe eines Set-Aufrufs implementiert werden.

internal class MyContext : DbContext
{
    public DbSet<Dictionary<string, object>> Blogs => Set<Dictionary<string, object>>("Blog");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
            "Blog", bb =>
            {
                bb.Property<int>("BlogId");
                bb.Property<string>("Url");
                bb.Property<DateTime>("LastUpdated");
            });
    }
}

Entitätstypen des Eigenschaftenbehälters können überall verwendet werden, wo ein normaler Entitätstyp verwendet wird, auch als eigener Entitätstyp. Sie haben jedoch bestimmte Einschränkungen: