섀도 및 인덱서 속성

섀도 속성은 .NET 엔터티 클래스에 정의되지 않았지만 EF Core 모델의 해당 엔터티 형식에 대해 정의된 속성입니다. 이러한 속성의 값과 상태는 변경 추적기에서만 유지됩니다. 섀도 속성은 데이터베이스에 매핑된 엔터티 형식에 노출되어서는 안 되는 데이터가 있는 경우에 유용합니다.

인덱서 속성은 .NET 엔터티 클래스의 인덱서에서 백업되는 엔터티 형식 속성입니다. .NET 클래스 인스턴스에서 인덱서로 액세스할 수 있습니다. 또한 CLR 클래스를 변경하지 않고 엔터티 형식에 속성을 추가할 수 있습니다.

외래 키 섀도 속성

섀도 속성은 규칙에 의해 검색되거나 명시적으로 구성된 외래 키 속성이 없을 때 규칙에 따라 모델에 추가되는 외래 키 속성에 가장 자주 사용됩니다. 관계는 탐색 속성으로 표시되지만 데이터베이스에서는 외래 키 제약 조건에 의해 적용되고 외래 키 열의 값은 해당 섀도 속성에 저장됩니다.

속성의 이름이 <navigation property name><principal key property name>으로 지정됩니다(주 엔터티를 가리키는 종속 엔터티의 탐색은 명명에 사용됩니다). 보안 주체 키 속성 이름이 탐색 속성의 이름으로 시작하는 경우 이름은 <principal key property name>입니다. 종속된 엔터티에 탐색 속성이 없는 경우 기본 또는 대체 키 속성 이름과 연결된 보안 주체 형식 이름이 해당 위치에 사용됩니다.

예를 들어 다음 코드 목록에서는 Post 엔터티에 BlogId 섀도 속성이 도입됩니다.

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

섀도 속성 구성

Fluent API를 사용하여 섀도 속성을 구성할 수 있습니다. Property<TProperty>(String)의 문자열 오버로드를 호출한 후에는 다른 속성에 대해 수행할 구성 호출을 연결할 수 있습니다. 다음 샘플에서는 BlogLastUpdated라는 CLR 속성이 없으므로 섀도 속성이 만들어집니다.

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;

섀도 속성은 EF.Property 정적 메서드를 통해 LINQ 쿼리에서 참조할 수 있습니다.

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

반환된 엔터티가 변경 추적기에서 추적되지 않으므로 추적 없음 쿼리 후에는 그림자 속성에 액세스할 수 없습니다.

인덱서 속성 구성

Fluent API를 사용하여 인덱서 속성을 구성할 수 있습니다. 메서드 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 메서드에 제공된 이름이 기존 인덱서 속성의 이름과 일치하는 경우 코드는 해당 기존 속성을 구성합니다. 엔터티 형식에 엔터티 클래스의 속성에 의해 지원되는 속성이 있는 경우 인덱서 속성은 인덱서를 통해서만 액세스해야 하므로 예외가 throw됩니다.

인덱서 속성은 위에 표시된 정적 메서드를 통해 EF.Property 또는 CLR 인덱서 속성을 사용하여 LINQ 쿼리에서 참조할 수 있습니다.

속성 모음 엔터티 형식.

인덱서 속성만 포함하는 엔터티 형식을 속성 모음 엔터티 형식이라고 합니다. 이러한 엔터티 형식에는 섀도 속성이 없으며 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");
            });
    }
}

속성 모음 엔터티 형식은 소유 엔터티 형식을 포함하여 일반 엔터티 형식이 사용되는 곳마다 사용할 수 있습니다. 그러나 다음과 같은 특정 제한 사항이 있습니다.