【问题标题】:Cannot insert explicit value for identity column in table when IDENTITY_INSERT is set to OFF."当 IDENTITY_INSERT 设置为 OFF 时,无法为表中的标识列插入显式值。”
【发布时间】:2019-03-10 18:59:11
【问题描述】:

对于我的 API,我将 Entity Framework Core 与代码优先迁移一起使用。我创建了一些运行良好的关系。现在,我添加了另一个关系(一对多),突然间我被这个错误打了耳光:

Cannot insert explicit value for identity column in table 'Companies' when IDENTITY_INSERT is set to OFF."

当然,我一定是做错了什么,但我就是想不通什么。我遇到了更多这样的问题,答案是“将 IDENTITY_INSERT 设置为 ON”,但这对我不起作用,因为 EF 正在处理所有事情。

我的公司类可以属于一个组:

public class Company
{
  // Primary key
  public int Id { get; set; }

  // The optional Id of a Group
  public int? GroupID { get; set; }

...

}

还有 Group 类:

public class Group
{
  // Primary key
  public int Id { get; set; }

  // Name of the Group
  public string Name { get; set; }

  // List of Companies in this group
  public IEnumerable<Company> Companies { get; set; }
}

用于处理 POST 的代码:

// POST api/groups
[HttpPost]
public async Task<IActionResult> Post([FromBody] Group group)
{
  try
  {
    if (ModelState.IsValid)
    {
      _context.Groups.Add(group);
      await _context.SaveChangesAsync();
      return CreatedAtRoute("GetGroup", new { id = group.Id }, group);
    }

    return BadRequest(ModelState);
  }
  catch (Exception e)
  {
    return BadRequest($"Unable to create: {e.Message}");
  }
}

在我的数据库中,所有列、索引和键都按预期创建,就像我在 API 中建立的所有其他一对多关系一样。但这个具体案例似乎最终以痛苦告终......

我要添加到数据库的类:

【问题讨论】:

  • 你能贴出代码吗,哪一部分出现异常?
  • 我已经更新了问题
  • 您需要确保实体 Company 的 EF 映射和表 Companies 的 DB Schema 对齐。如果您想使用 IDENTITY 进行插入,请确保表模式设置为 IDENTITY_INSERT 并且 已为 Identity Insert 配置了映射(这最后一部分可能是您的问题)。如果您不想使用它,请确保您在属性中传递了一个明确的值。
  • 错误非常简单。在尝试首先创建组时,您正在为您的组传递一个 id。 id 是一个自动递增的主键,所以 id 来自数据库,而不是你。不要设置明确的 id,你很好。
  • @ChrisPratt 听起来很简单,但问题是我没有设置显式 id 并且 EF 处理一切......

标签: sql-server asp.net-core entity-framework-core


【解决方案1】:

问题是 EF 没有提示知道 Company(在 Group 关系下)是否被显式插入,或者它是否应该使用数据库中预先存在的一个。

由于这些实例已与DbContext 断开连接,因此在 EF 尝试生成其 SQL 命令时,没有任何迹象表明它们是否存在于数据库中。

这里没有简单的方法,至少在我使用 EF Core 时没有。

您应该:

  1. 将您的代码更改为仅使用ID 而不是导航属性,以便尽可能避免这种情况,或者;
  2. 在保存所需数据之前(例如:在保存 Group 之前)更改您的代码以获取相关数据(例如:获取 Company 并将其附加到 Group)。

例如:

var companyDB = await context.Companies.SingleOrDefaultAsync(c => c.Id == group.Company.Id);
group.Company = companyDB;
context.Groups.Add(group);
await context.SaveChangesAsync();

是的,您要访问数据库两次。这就是我建议使用第一种方法的原因,这样您就可以避免这种提取,只需将Group 实体直接保存到数据库中。

但是,这并不禁止您将导航实例 Company 发送到您的视图。只需创建一些与您的数据库相关的实体相关类,以便您可以使用此实体类型加载/保存数据,并创建一个 Dto 对象,该对象将在需要时作为您的端点输入/输出。

可以使用 AutoMapper、手动 linq 或其他方法将一个绑定到另一个。

【讨论】:

  • 第二个解决方案是这个特定问题的实际解决方案。当发布一个with 公司列表的新组时,Company.GroupID 为 0,这是错误中提到的显式值。创建一个新组并向其中添加公司列表解决了这个问题。
  • @AndersonMatos 您能否在这种特殊情况下添加第一种方法的示例?
  • 您的意思是仅 ID 方法?您只需添加具有相应映射但没有任何导航属性的 ID 字段。
【解决方案2】:

这是因为您在设置为标识(自动增量)的列中传递了一些值。

我认为您正在插入的Group 实体的公司具有 ID 值,它试图将其作为子记录插入公司表中。它会引发错误。

【讨论】:

    【解决方案3】:

    在尝试保存与现有实体(属性Owner,指向Person)具有多对一关系的实体(例如Cat)时,我遇到了类似的问题。除了 a) 在保存实体 (Cat) 之前从数据库中获取关系 (Person),以及 b) 向实体 (Cat) 添加另一个属性 (int PersonId),我发现了我的想法最佳解决方案:c) 只坚持“导航”属性(不要创建额外的int &lt;&lt;Name&gt;&gt;Id 属性),当需要引用时,使用cat.Owner = dbContext.Person.Attach(new Person { Id = 123 }).Entity;

    【讨论】:

      【解决方案4】:

      Entity Framework 由于某种原因失去了跟踪,Entity Framework 需要重新建立对已经存在的实体的跟踪。

      您可以通过以下方式获取实体的跟踪状态:

      var entityTrackingState = _context.Entry(entity).State;
      

      您可以强制实体框架对现有实体进行跟踪:

      _context.Entry(untrackedEntity).State = EntityState.Unchanged;
      

      在哪里

      _context 
      

      是一个实体框架 DbContext。

      强制跟踪解决了我的问题,但它确实应该在 Entity Framework 丢失跟踪的地方进行调试。

      【讨论】:

        猜你喜欢
        • 2016-05-13
        • 2018-06-29
        • 2016-08-18
        • 2010-11-22
        • 2017-10-26
        • 1970-01-01
        • 2011-08-26
        相关资源
        最近更新 更多