![](/img/trans.png)
[英]What is the correct way to do many to many entity relation update in Entity framework core 6?
[英]What is the correct way to do many to many entity relation insert?
我正在使用 .net5 和 EntityFrameworkCore 5。
我在问题和类别之间有多对多的关系。
我正在使用第一代代码。
public class Question
{
public int Id { get; set; }
public string Title { get; set; }
public ICollection<Category> Categories { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Question> Questions { get; set; }
}
我想知道如何添加带有类别的问题。
我试过这个:
[HttpPost]
public async Task<ActionResult<Question>> PostQuestion(Question question)
{
question.Categories.Add(new Category() { Id = 1 });
_context.Questions.Add(question);
await _context.SaveChangesAsync();
return CreatedAtAction("GetQuestion", new { id = question.Id }, question);
}
我的数据库中有一个 ID 为 1 的类别。
但是我得到了这个例外
SqlException: Cannot insert explicit value for identity column in table 'Categories' when IDENTITY_INSERT is set to OFF.
做多对多实体关系插入的正确方法是什么?
真正正确和预期的方法是在将相关实体添加到“链接”集合之前将它们加载到上下文中。
假设您有一个现有相关实体键的列表:
var categoryIds = new[] { 1, 3, 4 };
然后您可以使用Find
方法将相应的实体加载到上下文中并获取它们的实例:
question.Categories = categoryIds
.Select(id => _context.Categories.Find(id))
.ToList();
缺点是它会进行N
次数据库往返来加载您可能并不真正需要的数据。
通过发出基于Contains
的查询,只需一个额外的数据库往返即可完成:
question.Categories = await _context.Categories
.Where(e => categoryIds.Contains(e.Id))
.ToListAsync();
如果您真的不想要相关实体,以下是其他一些方法。
如果上下文生存期仅限于该调用,那么您可以像尝试一样使用假(存根)实体,但您必须Attach
它们附加到上下文以让 EF Core 将它们视为现有而不是新的(如果您不这样做)不这样做:
question.Categories = categoryIds
.Select(id => _context.Attach(new Category { Id = id }))
.ToList();
另一种方法是直接在影子连接字典类型实体集中插入条目。 但它需要知道连接实体类型及其影子 FK 的常规名称,因此这是类型不安全的。
您还需要首先Add
实体才能使其临时密钥可用:
var entry = _context.Questions.Add(question);
然后对于所示的 model 你有
var joinEntityName = "CategoryQuestion";
var fromPK = nameof(Question.Id);
var fromFK = "QuestionsId";
var toFK = "CategoriesId";
实际上,这些可以从 EF Core 元数据中获取,这将使其更安全:
var navInfo = entry.Collection(e => e.Courses).Metadata as Microsoft.EntityFrameworkCore.Metadata.ISkipNavigation;
var joinEntityName = navInfo.JoinEntityType.Name;
var fromPK = navInfo.ForeignKey.PrincipalKey.Properties.Single().Name;
var fromFK = navInfo.ForeignKey.Properties.Single().Name;
var toFK = navInfo.Inverse.ForeignKey.Properties.Single().Name;
然后插入代码是:
var fromId = entry.CurrentValues[fromPK]; // the temp PK
db.Set<Dictionary<string, object>>(joinEntityName).AddRange(
categoryIds.Select(toId => new Dictionary<string, object>
{
{ fromFK, fromId },
{ toFK, toId },
}));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.