前言
不知我們是否思考過一個問題,在關系映射中對於導航屬性的訪問修飾符是否一定必須為public呢?如果從未想過這個問題,那么我們接下來來探討這個問題。
EF 6.x和EF Core 何種情況下必須配置映射關系?
在EF 6.x中我們創建如下示例類。
public partial class Customer { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public ICollection<Order> Orders { get; set; } = new List<Order>(); }
public class Order : BaseEntity { public int Quantity { get; set; } public string Code { get; set; } public decimal Price { get; set; } public int CustomerId { get; set; } public Customer Customer { get; set; } }
上述我們不顯式配置映射關系,EF和EF Core會根據約定來配置,同樣達到如我們期望的,無論是EF 6.x還是EF Core中通過Inlcude進行顯式加載有兩種方式,一種是基於字符串,另外一種則是通過lamda表達式的方式(命名空間存在於System.Data.Entity),接下來我們來看下:
using (var ctx = new EfDbContext()) { ctx.Database.Log = Console.WriteLine; var customers = ctx.Customers.Include(d => d.Orders).ToList(); };
這樣是我們一直以來最正常的操作,如前言所敘,那么導航屬性難道必須是public嗎?接下來我們來試試。我們嘗試將Orders導航屬性配置成如下私有的。
private ICollection<Order> Orders { get; set; } = new List<Order>();
因為其為私有,若通過lambda表達式肯定是訪問受限制,那么我們改為通過基於字符串的方式來顯式加載,如下:
using (var ctx = new EfDbContext()) { ctx.Database.Log = Console.WriteLine; var customers = ctx.Customers.Include("Orders").ToList(); };
如上則拋出異常找不到Orders導航屬性,是不是到此下結論而定導航屬性必須是public呢?訪問修飾符除了public,還有protected、internal以及protected internal。通過實踐驗證若導航屬性為private、protected訪問修飾符肯定不行,若為internal和protected internal則可以,前提是必須顯式配置映射關系,否則也不行,如下:
protected internal ICollection<Order> Orders { get; set; } = new List<Order>();
HasMany(p => p.Orders).WithRequired(p => p.Customer).HasForeignKey(k => k.CustomerId);
那么在EF Core是否也和EF 6.x一樣呢?我們繼續來看看在EF Core中的情況,示例類為Blog和Post,這兩個類已經在博客文章多次被用到,就不再給出,我們只關系導航屬性訪問修飾符的配置,如下:
private ICollection<Post> Posts { get; set; } = new List<Post>();
using (var context = new EFCoreDbContext()) { var blogs = context.Blogs.Include("Posts").ToList(); }
此時會同樣拋出異常,只不過異常信息大意是Posts不是Blog導航屬性的一部分,對於基於字符串的Include方法,導航屬性名稱要以點分隔開,最終結果還是是找不到Posts導航屬性,接下來我們將訪問修飾符改為internal看看。
internal ICollection<Post> Posts { get; set; } = new List<Post>();
using (var context = new EFCoreDbContext()) { var blogs = context.Blogs.Include(d => d.Posts).ToList(); //var blogs1 = context.Blogs.Include("Posts").ToList(); }
此時我們再來顯式配置映射關系則好使。
builder.HasMany(m => m.Posts)
.WithOne(o => o.Blog);
總結
對於EF和EF Core中通過Include方法進行顯式加載具體實現沒有去看源碼,完全通過實踐得到的結論是:無論是EntityFramework還是EntityFramework Core,在關系映射中導航屬性不一定必須是public修飾符,也可以為internal和protected internal,但是前提是必須顯式配置映射關系,否則將拋出無法找到導航屬性異常。