请先看dudu的文章
一般的多对多关系数据库表:
Mapping代码:
Code Snippet
- modelBuilder.Entity<BlogPost>()
- .HasMany(b => b.Categories)
- .WithMany(c => c.CategoryBlogPosts)
- .Map
- (
- m =>
- {
- m.MapLeftKey("BlogPostID");
- m.MapRightKey("CategoryID");
- m.ToTable("BlogPost_Category");
- }
- );
问题:如果以上满足不了我们的需求,比如我需要再关系表中增加对当前关系的其它信息,比如isactive,只是个举例,实际博客分类中也许并不需要这个字段。那么在CategoryA.CategoryBlogPosts中我想选取IsActive为true的文章就很痛苦了。
根本原因在于这里的多对多映射叫做direct multi to multi直接隐藏了关系表,我自然想到了用两个一对多来实现,本来多对多就是这样来的。
那么映射就变成下面这样了:
Code Snippet
- modelBuilder.Entity<BlogPost_Category>()
- .HasKey(it => it.BlogPostId)
- .HasKey(it => it.CategoryId);
- modelBuilder.Entity<BlogPost_Category>()
- .HasRequired(it => it.BlogPost)
- .WithMany(it=>it.BlogPostCategories)
- .HasForeignKey(it => it.BlogPostId).WillCascadeOnDelete(false);
- modelBuilder.Entity<BlogPost_Category>()
- .HasRequired(it => it.Category)
- .WithMany(c => c.BlogPost_Categorys)
- .HasForeignKey(it => it.CategoryId).WillCascadeOnDelete(false);
这里修改了之间关联的属性:
Code Snippet
- public virtual ICollection<BlogPost_Category> BlogPostCategories { get; set; }
- public virtual IEnumerable<Category> Categories
- {
- get { return BlogPostCategories.Where(it => it.IsActive == true).Select(it => it.Category); }
- }
Code Snippet
- public virtual ICollection<BlogPost_Category> BlogPost_Categorys { get; set; }
- public virtual IEnumerable<BlogPost> CategoryBlogPosts
- {
- get
- {
- return BlogPost_Categorys
- .Where(it => it.IsActive == true)
- .Select(it => it.BlogPost);
- }
- }
测试代码:
Code Snippet
- [TestMethod]
- public void GetBlogPostsActivedInCategory_Test()
- {
- var cat = _aggBlogSiteService.GetCategory(2);
- cat.CategoryBlogPosts.
- ToList()
- .ForEach(p =>
- {
- Console.WriteLine(string.Format("ID:{ 0}\tTitle:{ 1}", p.ID,
- p.Title));
- p.Categories.ToList().ForEach(c => Console.WriteLine(
- string.Format("Categories:\ttitle:{ 0}\tisActive:{ 1}"
- , c.Title
- , c.BlogPost_Categorys
- .Single(pc => pc.BlogPostId == p.ID && pc.CategoryId == c.ID).IsActive)));
- });
- }
测试数据:
结果测试编译不通过,查了下资料,原来当BlogPostID和CategoryID一起做主键时就无法再作为外键,感觉没道理啊,相信将来Ef会改进,原因也不清楚,有时间得好好研究,另外推荐一本书:
可惜我是忠实的CodeFirst Fans,等下个版本了。
新建另一个键当主键,不过显然是多余的,无所谓了(第三幅图中的ID就此由来~)。
测试结果:
最重要的是SQL脚本:
N'SELECT
[Project2].[ID] [ID],
[Project2].[Title] [Title],
[Project2].[C1] [C1],
[Project2].[ID1] [ID1],
[Project2].[BlogPostId] [BlogPostId],
[Project2].[CategoryId] [CategoryId],
[Project2].[IsActive] [IsActive]
(
[Limit1].[ID] [ID],
[Limit1].[Title] [Title],
[Extent2].[ID] [ID1],
[Extent2].[BlogPostId] [BlogPostId],
[Extent2].[CategoryId] [CategoryId],
[Extent2].[IsActive] [IsActive],
([Extent2].[ID] ) ( ) 1 [C1]
( (1)
[Extent1].[ID] [ID],
[Extent1].[Title] [Title]
[dbo].[Category] [Extent1]
[Extent1].[ID] = @p__linq__0 ) [Limit1]
[dbo].[BlogPost_Category] [Extent2] [Limit1].[ID] = [Extent2].[CategoryId]
) [Project2]
[Project2].[ID] , [Project2].[C1] ',N'@p__linq__0 ',@p__linq__0=2
呵呵,跟dudu的一样。。。Done