多对多Entity Framework Core如何插入

问题描述 投票:0回答:3

基于 MS 的教程:https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs= Fluent-api%2C Fluent-api-simple-key%2Csimple-key

这就是我们如何定义彼此具有多对多关系的 Post 和 Tag:

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }
    public ICollection<Post> Posts { get; set; }
}

当我运行数据库更新时,它已经能够生成三个表:

Post
Tag
PostTag
,它们具有主键
[PostId, TagId]
。总的来说,数据库架构正是我所想的。

但是,没有提到的是如何插入与

Post
有关系的
Tags
,反之亦然。

db.Posts.Add(new Post { Title="Title1", Content="Content1"});
db.Posts.Add(new Post { Title="Title2", Content="Content2"});
db.Tags.Add(new Tag { TagId="Tag1"});
db.Tags.Add(new Tag { TagId="Tag2"});
db.SaveChanges();

现在数据库每个表中有2条记录。我的问题是我们如何添加具有 tag1 和 tag2 的新帖子?我的下面的代码不起作用。有人可以指出哪里出了问题吗?或者我是否需要通过添加第三个实体来执行旧方法

PostTag

这是我的代码:

ICollection<Tag> tags = new List<Tag> {db.Tags.FirstOrDefault(i => i.TagId="Tag1"), db.Tags.FirstOrDefault(i => i.TagId="Tag2")};
db.Posts.Add(new Post { Title="Title3", Content="Content3", Tags=tags});

db.SaveChanges();

我得到的是异常,说它无法将 post3 插入数据库。

更新:感谢大家的帮助。发现我的真实代码犯了一个错误。这是我的实际代码:

vICollection<Tag> tags = new List<Tag> {db.Tags.FirstOrDefault(i => i.TagId="Tag1"), db.Tags.FirstOrDefault(i => i.TagId="Tag2")};
var post3 = new Post { Title="Title3", Content="Content3"};
db.Posts.Add(post3);
post3.Tags = tags; // wrong position, should be called before db.Posts.Add(post3)
db.SaveChanges();

错误是

post3.Tags = tags;
是在
db.Posts.Add(post3);
之后调用的。应该是这样的:

vICollection<Tag> tags = new List<Tag> {db.Tags.FirstOrDefault(i => i.TagId="Tag1"), db.Tags.FirstOrDefault(i => i.TagId="Tag2")};
var post3 = new Post { Title="Title3", Content="Content3"};
post3.Tags = tags; // correct position
db.Posts.Add(post3);
db.SaveChanges();

我还从我的代码中删除了 PostId。

c# asp.net-core entity-framework-core many-to-many
3个回答
4
投票

当您添加

Post
并指定
PostId
时,会出现以下错误:

无法在表“Posts”中插入显式标识列值 IDENTITY_INSERT 设置为 OFF。

只需删除

PostId
,代码就可以正常工作:

ICollection<Tag> tags = new List<Tag> {
           db.Tags.FirstOrDefault(i => i.TagId="Tag1"), 
           db.Tags.FirstOrDefault(i => i.TagId="Tag2")};
db.Posts.Add(new Post { Title="Title3", Content="Content3", Tags=tags});

db.SaveChanges();

1
投票

正如 @Rena 已经提到的,您的代码无法工作,因为您在创建

PostId=3
实体时设置主键值 (
Post
)。我只是添加一个改进点。

在下面的代码中,您将多次访问数据库以一次获取一个

Tag
-

ICollection<Tag> tags = new List<Tag> {db.Tags.FirstOrDefault(i => i.TagId="Tag1"), db.Tags.FirstOrDefault(i => i.TagId="Tag2")};

您可以一次性获取标签列表,例如-

var tags = db.Tags.ToList();

你的最终代码可能看起来像 -

var tags = db.Tags.ToList();
var post = new Post
{
    Title = "Title3",
    Content = "Content3",
    Tags = tags.Where.(i => i.TagId == "Tag1" || i.TagId == "Tag2")).ToList()
}
db.Posts.Add(post);
db.SaveChanges()

0
投票

避免额外的数据加载 - 使用附加

对其他人之前的解决方案的改进是,您可以避免从数据库中获取相关标签,只需将它们

Attach
添加到
DbContext

var tags = [
  new Tag { Id = "Id1" },
  new Tag { Id = "Id2" }
];

db.Posts.AttachRange(tags);
db.Posts.Add(new Post { Title="Title1", Content="Content1", Tags = tags});
db.SaveChanges();

请注意,只有当您已经拥有相关标签的 Id 并且不需要查询 Id 时,才可以进行此优化。如果您有有效的标签 ID,则提供的代码只会将新帖子和一些记录插入到连接表
这样,

Attach
开始跟踪处于
Unchanged
状态的标签,因此在保存更改时不会插入标签:

默认情况下使用“未更改”状态开始跟踪给定实体和可从给定实体访问的条目

© www.soinside.com 2019 - 2024. All rights reserved.