asp.net core系列 28 EF模型配置(字段,構造函數,擁有實體類型)
一. 支持字段
EF允許讀取或寫入字段而不是一個屬性。在使用實體類時,用面向對象的封裝來限制或增強應用程序代碼對數據訪問的語義時,這可能很有用。無法使用數據註釋配置。除了約定,還可以使用Fluent API為屬性配置支持字段。
1.1 約定
public class Blog { // _<camel-cased property name> private string _url; public int BlogId { get; set; } public string Url {get { return _url; } set { _url = value; } } }
1.2 Fluent API
modelBuilder.Entity<Blog>() .Property(b => b.Url) .HasField("_validatedUrl"); public class Blog { private string _validatedUrl; public string Url {get { return _validatedUrl; } } public void SetUrl(string url) { //... _validatedUrl = url; } }
二. 構造函數
從開始 EF Core 2.1,可以定義帶參數的構造函數,並在創建實體實例時讓EF Core調用此構造函數。構造函數參數可以綁定到映射屬性,或綁定到各種服務,以方便延遲加載等行為。
2.1 帶參的構造函數
下面代碼演示帶參數的構造函數,並且設置只讀屬性,外部調用該類時,只能通過構造函數傳入實體值。
public class Blog { public Blog(int id, string name, string author) { Id = id; Name = name; Author = author; } public int Id { get; private set; } public string Name { get; private set; } public string Author { get; private set; } public ICollection<Post> Posts { get; } = new List<Post>(); }
別外使用私有setter的另一種方法是使屬性真正只讀,並在OnModelCreating中添加更明確的映射。
public class Blog { private int _id; public Blog(string name, string author) { Name = name; Author = author; } public string Name { get; } public string Author { get; } public ICollection<Post> Posts { get; } = new List<Post>(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>( b => { b.HasKey("_id"); b.Property(e => e.Author); b.Property(e => e.Name); }); }
2.2 註入服務
EF Core還可以將“服務”註入實體類型的構造函數中。例如,可以註入以下內容:
DbContext - 當前上下文實例,也可以作為派生的DbContext類型鍵入
ILazyLoader- 延遲加載服務
Action<object, string>- 一個延遲加載的委托
IEntityType - 與此實體類型關聯的EF Core元數據
例如,註入的DbContext可用於選擇性地訪問數據庫以獲得關於相關實體的信息而無需全部加載它們。在下面的示例中,這用於獲取Blog博客中的Posts帖子數量:
public class Blog { public Blog() { } private Blog(BloggingContext context) { Context = context; } private BloggingContext Context { get; set; } public int Id { get; set; } public string Name { get; set; } public string Author { get; set; } public ICollection<Post> Posts { get; set; } //獲取帖子數量 public int PostsCount => Posts?.Count ?? Context?.Set<Post>().Count(p => Id == EF.Property<int?>(p, "BlogId")) ?? 0; }
有一些需要註意:
(1)構造函數是私有的,因為它只由EF Core調用,並且還有另一個通用的公共構造函數。
(2)使用註入服務的代碼(即EF上下文)防禦它為null,處理EF Core未創建實例的情況。
(3)因為服務存儲在讀或寫屬性中,所以當實體附加到新的上下文實例時,它將被重置。
2.2示例演示,沒有成功,Blog帶參的構造函數為私有,無法調用, context上下文總為null,以後在了解。
三.擁有的實體類型
該功能是在 EF Core 2.0 中的新增功能。是指:當一個實體類中包含導航屬性(實體類型引用屬性),並對導航屬性進行建模,這個導航屬性類被稱為“擁有實體類型”。而包含“擁有實體類型”的類叫:所有者。
3.1 顯示配置
EF Core中的所有實體類型永遠不會按照約定包含在模型中。可以使用OwnsOne
在(使用EF Core 2.1中的新增功能)OnModelCreating
中使用或用註釋類型OwnedAttribute
將類型配置為擁有類型。
下面示例中StreetAddress
是一個沒有標識屬性的類型。 它用作 Order 類型的屬性來指定特定訂單的發貨地址。
//擁有實體類型 [Owned] public class StreetAddress { public string Street { get; set; } public string City { get; set; } } //所有者 public class Order { public int Id { get; set; } public StreetAddress ShippingAddress { get; set; } } public class BloggingContext : DbContext { public BloggingContext(DbContextOptions<BloggingContext> options) : base(options) { } public DbSet<Order> Order { get; set; }
}
使用EF基於數據模型(Order)創建數據庫,如下圖所示。
還可以使用該OwnsOne
方法在OnModelCreating
中
指定ShippingAddress
屬性,是Order
實體類型的擁有實體,並根據需要配置其他方面。
modelBuilder.Entity<Order>().OwnsOne(p => p.ShippingAddress);
如果ShippingAddress
屬性在Order
實體中為私有屬性,則可以使用的字符串版本OwnsOne
方法:
modelBuilder.Entity<Order>().OwnsOne(typeof(StreetAddress), "ShippingAddress");
3.2 隱含鍵
OwnsOne通過引用導航配置或發現的擁有類型始終與所有者具有一對一的關系,因此擁有實體不需要自己的鍵值,因為外鍵值是唯一的。在前面的示例中,StreetAddress
類型不需要定義鍵屬性。擁有實體類型的實例鍵值與所有者實例的鍵值相同。
3.3 擁有的集合類型
要配置擁有的集合類型,使用OwnsMany
在OnModelCreating
中使用。
但是,主鍵不會按約定配置,因此需要明確指定。下面代碼演示擁有的集合類型的關鍵代碼。
public class Distributor { public int Id { get; set; } public ICollection<StreetAddress> ShippingCenters { get; set; } } modelBuilder.Entity<Distributor>().OwnsMany(p => p.ShippingCenters, a => { // 給ShippingCenters表設置一個主鍵 a.Property<int>("Id"); //給ShippingCenters表設置一個外鍵 a.HasForeignKey("DistributorId"); //設置復合主鍵 a.HasKey("DistributorId", "Id"); });
使用EF基於數據模型(Distributor)創建數據庫,如下圖所示(一對多的關系)。Distributor_ShippingCenters表的ID為主鍵,DistributorId為外鍵。
關於擁有的實體類型的更多介紹參考官方文檔,這裏不是介紹。關於“EF模型配置系列”的很多功能,都是基於code first模式。
參考文獻:
官方文檔:EF支持字段
EF構造函數
EF擁有的實體類型
擁有實體類型的完整案例
asp.net core系列 28 EF模型配置(字段,構造函數,擁有實體類型)