EntityFramework 實體拆分和表拆分
之前有人問過 EF 如何進行實體拆分和表拆分?我記得當時認為不可能,理由忘記了,後來又有人發了一段配置截圖,發現原來是可以的,不記錄的東西容易忘掉,關於 EF 實體拆分和表拆分,下面是自己的一些整理。
兩個概念:
- 實體拆分:一個實體拆分成多個表,如 Blog 實體,可以拆分成 Blogs 和 BlogDetails 兩個表。
- 表拆分:一個表拆分成多個實體,如 Posts 表,可以拆分成 Post 和 PostDetail 兩個實體。
1. 實體拆分
配置程式碼:
public class BloggingContext : DbContext { public BloggingContext() : base("name=ef_split_dbcontext") { } public DbSet<Blog> Blogs { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Blog>() .Map(m => { m.Properties(t => new { t.Id, t.Title, t.Url }); m.ToTable("Blogs"); }) .Map(m => { m.Properties(t => new { t.Id, t.Remark }); m.ToTable("BlogDetails"); }); base.OnModelCreating(modelBuilder); } } public class Blog { [Key] public int Id { get; set; } public string Title { get; set; } public string Url { get; set; } public string Remark { get; set; } }
對映效果:
測試程式碼:
using (var context=new BloggingContext())
{
context.Blogs.Add(new Blog { Remark = "", Title = "EntityFramework 實體拆分和表拆分", Url = "http://www.cnblogs.com/xishuai/p/ef-entity-table-splitting.html" });
context.SaveChanges();
}
測試結果為 Blogs 和 BlogDetails 表中,分別產生一條資料,即使 Remark 的值為空。
2. 表拆分
配置程式碼:
public class BloggingContext : DbContext { public BloggingContext() : base("name=ef_split_dbcontext") { } public DbSet<Post> Posts { get; set; } public DbSet<PostDetail> PostDetails { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Post>() .HasKey(t => t.PostId); modelBuilder.Entity<PostDetail>() .HasKey(t => t.PostId); modelBuilder.Entity<PostDetail>() .HasRequired(t => t.Post) .WithRequiredPrincipal(t => t.PostDetail); modelBuilder.Entity<Post>().ToTable("Posts"); modelBuilder.Entity<PostDetail>().ToTable("Posts"); base.OnModelCreating(modelBuilder); } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public virtual PostDetail PostDetail { get; set; } } public class PostDetail { public int PostId { get; set; } public string Remark { get; set; } public virtual Post Post { get; set; } }
對映效果:
測試程式碼:
using (var context=new BloggingContext())
{
context.Posts.Add(new Post { Title="EntityFramework 實體拆分和表拆分"});
context.PostDetails.Add(new PostDetail { Remark=""});
context.SaveChanges();
}
測試結果為 Posts 表中產生一條資料,注意對映配置中的這段程式碼:modelBuilder.Entity<PostDetail>().HasRequired(t => t.Post).WithRequiredPrincipal(t => t.PostDetail);
,我們一般在外來鍵配置的時候會用到 HasRequired,Required 表示的意思是必須,還有一種寫法是:modelBuilder.Entity<PostDetail>().HasOptional(t => t.Post).WithOptionalPrincipal(t => t.PostDetail);
,關鍵詞 Optional,但對映會抱下面錯誤:The entity types 'Post' and 'PostDetail' cannot share table 'Posts' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them.
其實我的想法是,在上面測試程式碼中,用了兩個 Add(而不是一個 Add,然後用 PostDetail 屬性賦值),那會不會在 Posts 表中產生兩條資料,但顯然沒有,因為我們在對映配置的時候,使用的是 Required,可以理解為“強制合併為一個表”,不論你怎麼新增資料,都會只新增一條資料,另外,需要注意的是,在上面表拆分示例中,主實體是 Post,所以,如果只有 context.PostDetails.Add(new PostDetail { Remark=""});
,會抱下面錯誤:Invalid data encountered. A required relationship is missing. Examine StateEntries to determine the source of the constraint violation.