Many-to-many relationships in EF Core 2.0 – Part 1: The basics
转载这个系列的文章,主要是因为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的更多相关文章
- 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. ...
- 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 ...
- 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 ...
- EF Core 1.0 和 SQLServer 2008 分页的问题
EF Core 1.0 在sqlserver2008分页的时候需要指定用数字分页. EF Core1.0 生成的分页语句中使用了 Featch Next.这个语句只有在SqlServer2012的时候 ...
- 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 ...
- EF Core 1.0中使用Include的小技巧
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:由于EF Core暂时不支持Lazy Loading,所以利用Include来加载额外 ...
- .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 ...
- EF Core 2.0 新特性
前言 目前 EF Core 的最新版本为 2.0.0-priview1-final,所以本篇文章主要是针对此版本的一些说明. 注意:如果你要在Visual Studio 中使用 .NET Core 2 ...
- 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 ...
随机推荐
- java通过超链接和servlet配置实现服务器文件下载
1.在页面上面我们可以简单的写成: <td align="center""> <a href="<c:url value="/ ...
- vim命令“=”、“d”、“y”的用法(结合光标移动命令,一些场合会非常方便)
vim有许多命令,网上搜有一堆贴子.文章列举出各种功能的命令. 对于“=”.“d”.“y”,我在无意中发现了它们所具有的相同的一些用法,先举以下三个例子: =nG dnG ynG 其中,n为行号.注意 ...
- 从官网下载mod_jk.so
最近在搞apache和tomcat集群,这里就要用到mol_jk.so,在官网找了很久才知道在哪里下载,英语是硬伤啊. 1.打开tomcat官网 http://tomcat.apache.org/ 2 ...
- js 两个小括号 ()() 的用法
实现一个函数fn, 使fn(1)(2)的结果为两个参数的和,刚开始没反应过来,其实细细一想第二个括号就是函数再调用的问题,废话不多说,代码奉上: var fn = function(n) { func ...
- html发展史简介(摘抄)
1993年,IETF,Internet工程任务组(Internet Engineering Task Force)的简写.IETF又叫互联网工程任务组,成立于1985年底,是全球互联网最具权威的技术标 ...
- 新手必需用!大道至简的前端编辑器Sublime Text
很多人在进入学习前端的时候(包括我自己),除了选择学习合适的技术,还需要一个得(自)心(己)应(喜)手(欢)的开发工具,一个得心应手的开发工具除了可以令你的效率大大提高,也可以令你在写代码的时候,心情 ...
- EasyUI combobox 多选及回显赋值
multiple boolean 决定是否支持多项选择. $('#cc').combobox({ url:'combobox_data.json', multiple:true, //支持多选 val ...
- SQL Server中的游标CURSOR
游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集合的.而游标打破了这一规则,游标使得我们思考方式变为逐行进行.对于类C的开发人员来着,这样的思考方式会更加舒服. 正常面向集合的思维方式是: ...
- unity3d代码优化标准
转载自:https://blog.csdn.net/m0_37283423/article/details/84378384 代码优化 ● 尽可能使用for来代替foreach:每次foreach会产 ...
- react-native-splash-screen
react-native-splash-screen在GitHub上的地址:https://github.com/crazycodeboy/react-native-splash-screen rea ...