Shadow 和 Indexer 屬性
陰影屬性是您 .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; }
}
設定陰影屬性
您可以使用 Fluent API 來設定陰影屬性。 呼叫 的 Property<TProperty>(String)字串多載之後,您可以鏈結針對其他屬性的任何組態呼叫。 在下列範例中,由於 Blog
沒有名為 LastUpdated
的 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
方法的名稱符合現有索引器屬性的名稱,則程式代碼會設定該現有屬性。 如果實體類型具有屬性,而屬性是由實體類別上的屬性所支援,則會擲回例外狀況,因為索引器屬性只能透過索引器存取。
您可以透過 EF.Property
如上所示的靜態方法,或使用 CLR 索引器屬性,在 LINQ 查詢中參考索引器屬性。
屬性包實體類型
只包含索引器屬性的實體類型稱為屬性包實體類型。 這些實體類型沒有陰影屬性,EF 會改為建立索引器屬性。 目前僅 Dictionary<string, object>
支援作為屬性包實體類型。 它必須設定為具有唯一名稱的共用類型實體類型,而且必須使用呼叫實Set
作對應的DbSet
屬性。
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");
});
}
}
無論使用一般實體類型,都可以使用屬性包實體類型,包括作為擁有的實體類型。 不過,它們確實有某些限制:
- 它們不能有陰影屬性。
- 不支援索引器導覽
- 不支持繼承
- 某些關聯性模型建置 API 缺少共用類型實體類型的多載
- 其他類型無法標示為屬性包