【问题标题】:What is the correct way to do many to many entity relation insert?做多对多实体关系插入的正确方法是什么?
【发布时间】:2021-03-02 23:11:10
【问题描述】:

我正在使用 .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.

进行多对多实体关系插入的正确方法是什么?

【问题讨论】:

  • 此错误表明您正在为身份朋友指定一个 id - 尝试消除那里的 id =1 并查看它是如何工作的。
  • @KeithNicholas 但是如果一个问题可以有多个类别并且如果我不想重复这些类别,我需要有一个多对多的关系。我错了?
  • @ChrisB.Behrens 但是如何将问题链接到某个类别?
  • 您将捕获生成的 Id 作为 save 命令的结果。试一试,如果这不起作用,请更新问题。

标签: c# .net entity-framework entity-framework-core


【解决方案1】:

真正正确和预期的方法是在将相关实体添加到“链接”集合之前将它们加载到上下文中。

假设您有一个现有相关实体键的列表:

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);

那么对于显示的模型你有

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 },
    }));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-05-23
    • 2020-01-26
    • 1970-01-01
    相关资源
    最近更新 更多