转载这个系列的文章,主要是因为EF Core 2.0在映射数据库的多对多关系时,并不像老的EntityFramework那样有原生的方法进行支持,希望微软在以后EF Core的版本中加入原生支持多对多关系的Fluent API,这样这个系列的文章就不需要看了~

As of EF Core 2.0, many-to-many relationships without an explicitly mapped join table are not supported. However, all is not lost. In this series of posts I will show:

  • Mapping many-to-many relationships with a join entity/table
  • Abstracting/hiding the join entity
    • In a simple way for read-only access to the relationship
    • In a more involved way that allows entities to be added and removed from each end

Limitations

Before going any further I want to be clear about two limitations with the approach used in all these posts:

  • EF Core doesn’t know about un-mapped properties. This means that queries must still be written in terms of the join entity.
  • The join entity is not gone; it’s still in application code and still mapped. It’s just that normal interactions with the model, such as those in your application, do not use it.

Addressing these limitations requires changes to the internals of EF Core. I don’t expect the limitations to go away until at least some parts of GitHub issue 1368 are implemented.

The model

A good example of a many-to-many relationship is blogs and tags. Every blog can have many tags, and every tag can be associated with many blogs. A typical way to model this is:

public class Post
{
public int PostId { get; set; }
public string Title { get; set; } public ICollection<Tag> Tags { get; } = new List<Tag>();
} public class Tag
{
public int TagId { get; set; }
public string Text { get; set; } public ICollection<Post> Posts { get; } = new List<Post>();
}

Modeling with a join entity

This cannot be mapped directly using foreign keys–each post would need multiple FK values for each tag, and vice-versa. Instead, another entity type is needed to bridge the gap and hold all the FK pairs. In a relational database this is often called a “join table”, and we will map it to a join entity:

public class PostTag
{
public int PostId { get; set; }
public Post Post { get; set; } public int TagId { get; set; }
public Tag Tag { get; set; }
}

This entity type has two FKs each associated with a navigation property one pointing to one side of the relationship (Post) and the other to the other side of the relationship (Tag). When we want to associate a Post with a Tag, we create a new PostTag instance and set the navigation properties to point to the Post and the Tag. Equivalently, we could set the PostId FK to the PK value of the Post and the TagId FK to the PK value to the Tag.

To use this join entity we need to update the original entity types to map through it:

public class Post
{
public int PostId { get; set; }
public string Title { get; set; } public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
} public class Tag
{
public int TagId { get; set; }
public string Text { get; set; } public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
}

Configuring the join entity type

The join entity type needs to have a key defined since there is no key that EF can figure out by convention. Since pairs of PostId and TagId values are unique, we can use these pairs as a composite key for the entity:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });
}

The actual relationships don’t need to be configured explicitly in this case because they can be figured out by convention.

Using the many-to-many relationship

Let’s write a little console application to show this working. First, we need a DbContext:

public class MyContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"); protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });
}
}

I’m using SQL Server LocalDb as a database provider, but this code should work the same with any database provider.

Next, here is a little test application that manipulates the many-to-many relationship in various ways:

public class Program
{
public static void Main()
{
using (var context = new MyContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated(); var tags = new[]
{
new Tag { Text = "Golden" },
new Tag { Text = "Pineapple" },
new Tag { Text = "Girlscout" },
new Tag { Text = "Cookies" }
}; var posts = new[]
{
new Post { Title = "Best Boutiques on the Eastside" },
new Post { Title = "Avoiding over-priced Hipster joints" },
new Post { Title = "Where to buy Mars Bars" }
}; context.AddRange(
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] },
new PostTag { Post = posts[], Tag = tags[] }); context.SaveChanges();
} using (var context = new MyContext())
{
var posts = LoadAndDisplayPosts(context, "as added"); posts.Add(context.Add(new Post { Title = "Going to Red Robin" }).Entity); var newTag1 = new Tag { Text = "Sweet" };
var newTag2 = new Tag { Text = "Buzz" }; foreach (var post in posts)
{
var oldPostTag = post.PostTags.FirstOrDefault(e => e.Tag.Text == "Pineapple");
if (oldPostTag != null)
{
post.PostTags.Remove(oldPostTag);
post.PostTags.Add(new PostTag { Post = post, Tag = newTag1 });
}
post.PostTags.Add(new PostTag { Post = post, Tag = newTag2 });
} context.SaveChanges();
} using (var context = new MyContext())
{
LoadAndDisplayPosts(context, "after manipulation");
}
} private static List<Post> LoadAndDisplayPosts(MyContext context, string message)
{
Console.WriteLine($"Dumping posts {message}:"); var posts = context.Posts
.Include(e => e.PostTags)
.ThenInclude(e => e.Tag)
.ToList(); foreach (var post in posts)
{
Console.WriteLine($" Post {post.Title}");
foreach (var tag in post.PostTags.Select(e => e.Tag))
{
Console.WriteLine($" Tag {tag.Text}");
}
} Console.WriteLine(); return posts;
}
}

Summarizing this application, it:

  • Deletes any stale test database and creates a new one
  • Creates some Posts and some Tags and then creates join entities to associate them
  • Saves all the Posts and Tags and their relationships to the database
  • Loads the entities in a new context and displays them
  • Manipulates the relationships:
    • A new Post is created and tracked
    • Every “Pineapple” Tag is removed and replaced with a “Sweet” Tag
    • Every Post gets a new “Buzz” Tag
  • These are again saved, re-read, and displayed.

On my machine, this results in the following output:

Dumping posts as added:
Post Best Boutiques on the Eastside
Tag Golden
Tag Pineapple
Post Avoiding over-priced Hipster joints
Tag Girlscout
Tag Cookies
Post Where to buy Mars Bars
Tag Golden
Tag Pineapple
Tag Girlscout
Tag Cookies Dumping posts after manipulation:
Post Best Boutiques on the Eastside
Tag Golden
Tag Sweet
Tag Buzz
Post Avoiding over-priced Hipster joints
Tag Girlscout
Tag Cookies
Tag Buzz
Post Where to buy Mars Bars
Tag Golden
Tag Girlscout
Tag Cookies
Tag Sweet
Tag Buzz
Post Going to Red Robin
Tag Buzz Press any key to continue . . .

So that’s simple many-to-many relationship with a join entity. In the next post we’ll start hiding aspects of the join entity.

原文链接

Many-to-many relationships in EF Core 2.0 – Part 1: The basics的更多相关文章

  1. Many-to-many relationships in EF Core 2.0 – Part 2: Hiding as IEnumerable

    In the previous post we looked at how many-to-many relationships can be mapped using a join entity. ...

  2. Many-to-many relationships in EF Core 2.0 – Part 3: Hiding as ICollection

    In the previous post we ended up with entities that hide the join entity from the public surface. Ho ...

  3. Many-to-many relationships in EF Core 2.0 – Part 4: A more general abstraction

    In the last few posts we saw how to hide use of the join entity from two entities with a many-to-man ...

  4. EF Core 1.0 和 SQLServer 2008 分页的问题

    EF Core 1.0 在sqlserver2008分页的时候需要指定用数字分页. EF Core1.0 生成的分页语句中使用了 Featch Next.这个语句只有在SqlServer2012的时候 ...

  5. ASP.NET Core 开发-Entity Framework (EF) Core 1.0 Database First

    ASP.NET Core 开发-Entity Framework Core 1.0 Database First,ASP.NET Core 1.0 EF Core操作数据库. Entity Frame ...

  6. EF Core 1.0中使用Include的小技巧

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:由于EF Core暂时不支持Lazy Loading,所以利用Include来加载额外 ...

  7. .NET Core 1.0、ASP.NET Core 1.0和EF Core 1.0简介

    .NET Core 1.0.ASP.NET Core 1.0和EF Core 1.0简介 英文原文:Reintroducing .NET Core 1.0, ASP.NET Core 1.0, and ...

  8. EF Core 2.0 新特性

    前言 目前 EF Core 的最新版本为 2.0.0-priview1-final,所以本篇文章主要是针对此版本的一些说明. 注意:如果你要在Visual Studio 中使用 .NET Core 2 ...

  9. EF Core 2.0使用MsSql/Mysql实现DB First和Code First

    参考地址 EF官网 ASP.NET Core MVC 和 EF Core - 教程系列 环境 Visual Studio 2017 最新版本的.NET Core 2.0 SDK 最新版本的 Windo ...

随机推荐

  1. MySQL查询(未完结)

    MySql查询 单表查询: 查询所有字段 SELECT * FROM 表名; '*' 代表所有字段 查询指定字段 SELECT 字段名1, 字段名2 FROM 表名; 按照指定条件查询记录 1. 查询 ...

  2. chrome中常用的快捷键

    ctrl+n 新建窗口ctrl+shift+n 无痕模式新建窗口ctrl+t 打开新标签页ctrl+shift+t 打开最近关闭的标签页ctrl+tab 标签页之间切换ctrl+w/ctrl+F4 关 ...

  3. swoole 创建tcp服务器

    server.php <?php /** * 创建tcp服务器 * Date: 2019/1/15 */ $serv = new swoole_server('127.0.0.1', 9501) ...

  4. Postfix的工作原理

    传统的Sendmail将所有功能都集中在同一个程序里,这种结构我们称之为“单体式设计”(monolithic).Postfix采用专职负责的策略,不同的功能分别交由不同的专门程序处理,这种结构称为“模 ...

  5. Long Wei information technology development Limited by Share Ltd interview summary.

    Long Wei information technology development Limited by Share Ltd interview summary. I take part in c ...

  6. 沉淀,再出发——在Hadoop集群的基础上搭建Spark

    在Hadoop集群的基础上搭建Spark 一.环境准备 在搭建Spark环境之前必须搭建Hadoop平台,尽管以前的一些博客上说在单机的环境下使用本地FS不用搭建Hadoop集群,可是在新版spark ...

  7. Linux--sudo权限讲解

    sudo简介:sudo是linux系统管理指令,是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具,如halt,reboot,su等等.这样不仅减少了root用户的登录 和管理时间,同 ...

  8. SAP UI5和微信小程序对比之我见

    今天继续由SAP成都研究院著名的菜园子小哥Wang Cong,给大家分享他作为一个SAP前端人员是如何看待SAP UI5和微信小程序的异同点的. 关于Wang Cong种菜的手艺,大家请移步到他以前的 ...

  9. 单片机课程设计>八音盒

    2017—2018学年度第一学期 <单片机原理及应用>作品考试 八音盒 2017-2018-1<单片机原理及应用>作品设计提交文档 一.作品设计目的 1.利用51单片机的各个部 ...

  10. NGUI UILabel文字宽度和 UITweener

    做个记录 方便别人和自己以后查找. NGUI UILabel   文字宽度 高度 mLabel.GetComponent<UILabel>().getLabWidth()   mLabel ...