Свойства теневого и индексатора

Свойства тени — это свойства, которые не определены в классе сущностей .NET, но определены для этого типа сущности в модели EF Core. Значение и состояние этих свойств сохраняются исключительно в средстве отслеживания изменений. Свойства тени полезны, если в базе данных есть данные, которые не должны предоставляться в сопоставленных типах сущностей.

Свойства индексатора — это свойства типа сущности, которые поддерживаются индексатором в классе сущностей .NET. Доступ к им можно получить с помощью индексатора в экземплярах классов .NET. Он также позволяет добавлять дополнительные свойства в тип сущности, не изменяя класс CLR.

Свойства тени внешнего ключа

Теневые свойства чаще всего используются для свойств внешнего ключа, где они добавляются в модель по соглашению, когда свойство внешнего ключа не найдено по соглашению или явно настроено. Связь представлена свойствами навигации, но в базе данных она применяется ограничением внешнего ключа, а значение для столбца внешнего ключа хранится в соответствующем теневом свойстве.

Свойство будет названо <navigation property name><principal key property name> (навигация по зависимой сущности, которая указывает на основную сущность, используется для именования). Если имя основного ключа начинается с имени свойства навигации, то имя будет просто <principal key property name>. Если в зависимой сущности нет свойства навигации, то имя основного типа, сцепленное с именем первичного или альтернативного ключа, используется в своем месте <principal type name><principal key property name>.

Например, следующий список кода приведет к созданию теневого BlogId свойства для сущности Post :

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; }
}

Настройка свойств тени

Api Fluent можно использовать для настройки свойств тени. После вызова строковой перегрузки Property<TProperty>(String)можно выполнить цепочку вызовов конфигурации для других свойств. В следующем примере, так как Blog не имеет свойства CLR с именем LastUpdated, создается теневое свойство:

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; }
}

Если имя, предоставленное Property методу, соответствует имени существующего свойства (теневое свойство или одно определенное в классе сущностей), код настраивает это существующее свойство, а не вводит новое теневое свойство.

Доступ к свойствам тени

Значения теневых ChangeTracker свойств можно получить и изменить с помощью API:

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

Свойства тени можно ссылаться в запросах LINQ с помощью EF.Property статического метода:

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

Не удается получить доступ к теневым свойствам после запроса без отслеживания, так как возвращаемые сущности не отслеживаются средство отслеживания изменений.

Настройка свойств индексатора

Api Fluent можно использовать для настройки свойств индексатора. После вызова метода IndexerPropertyможно выполнить цепочку вызовов конфигурации для других свойств. В следующем примере определен индексатор, Blog который будет использоваться для создания свойства индексатора.

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;
    }
}

Если имя, предоставленное IndexerProperty методу, соответствует имени существующего свойства индексатора, код настроит это существующее свойство. Если тип сущности имеет свойство, которое поддерживается свойством класса сущности, исключение создается, так как свойства индексатора должны быть доступны только с помощью индексатора.

Свойства индексатора можно ссылаться на запросы LINQ через статический EF.Property метод, как показано выше или с помощью свойства индексатора CLR.

Типы сущностей контейнера свойств

Типы сущностей, содержащие только свойства индексатора, называются типами сущностей контейнера свойств. Эти типы сущностей не имеют теневых свойств, а EF создает свойства индексатора. В настоящее время поддерживается только Dictionary<string, object> в качестве типа сущности контейнера свойств. Он должен быть настроен как тип сущности общего типа с уникальным именем, и соответствующее DbSet свойство должно быть реализовано Set с помощью вызова.

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");
            });
    }
}

Типы сущностей контейнера свойств можно использовать везде, где используется обычный тип сущности, включая тип собственной сущности. Однако у них есть определенные ограничения: