Entity Framework 核心代码首先在保存之前更改添加的实体值导致标识插入错误

Entity Framework Core Code First changing added entities values before save causes identity insert error

我在保存添加的实体之前设置值时遇到问题。当我不更改实体值时,它会毫无问题地保存。

我得到的错误是这样的:

SqlException: Cannot insert explicit value for identity column in table 'TableName' when IDENTITY_INSERT is set to OFF.

我正在使用 .net Core 2.1,我的 nuget 包都是最新的。

下面的代码在 Entity Framework .NET Framework

中运行良好
public override int SaveChanges()
{
    try
    {
        int userId = currentUser.UserId;
        IEnumerable<EntityEntry> addedEntities = ChangeTracker.Entries()
                      .Where(e => e.State == EntityState.Added && e.CurrentValues.ToObject() is ModelBase)
                      .Select(e => e);

        foreach (var e in addedEntities)
        {
            var clE = e.CurrentValues.ToObject() as ModelBase;

            if (clE != null)
            {
                clE.CreatedOn = DateTime.Now;
                clE.CreatedBy = userId;
            }

            // When I delete this line, it works without a problem
            // but it doesn't set the values obviously
            e.CurrentValues.SetValues(clE);
        }

        return base.SaveChanges();
    }
    catch (Exception dbEx)
    {
        Exception raise = dbEx;
        throw raise;
    }
}

知道我做错了什么吗?

谢谢 克里斯

ToObject 方法的文档指出:

Creates an instance of the entity type and sets all its properties using the values from this object.

换句话说,该方法创建了一个 new 实体实例,并用被跟踪实体的当前值填充它。

请注意,在将实体添加到上下文时,EF 会为身份 PK 属性 生成临时值,因此 ToObject 也会复制该值。

然后SetValues复制回传递对象的所有属性,包括 PK,但现在PK值被标记为持久化(如果你将 PK 值明确设置为不同于默认值 0 的某个值。

所有这一切的最终结果是 EF 将尝试将显式值插入到 PK 密钥中,该密钥通常被禁用(需要特殊启用),因此出现异常。

解决方法是不使用ToObject / SetValues,而是修改实际被跟踪的实体,可以通过Entity访问 属性:

var addedEntities = ChangeTracker.Entries()
    .Where(e => e.State == EntityState.Added)
    .Select(e => e.Entity)
    .OfType<ModelBase>();

foreach (var e in addedEntities)
{
    // Here you modify the actual entity instance, so the values
    // you set will be considered by the base.SaveChanges() call
    e.CreatedOn = DateTime.Now;
    e.CreatedBy = userId;
}