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

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

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

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

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

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

Например, следующий листинг кода приведет к BlogId тому, что свойство shadow будет представлено в сущности 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, создается свойство shadow:

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 , совпадает с именем существующего свойства (теневое свойство или свойство, определенное в классе сущностей), код настраивает это существующее свойство, а не вводит новое свойство тени.

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

Значения свойств shadow можно получить и изменить с помощью 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");
            });
    }
}

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