使用实体框架插入时设置 Id 字段

Posted

技术标签:

【中文标题】使用实体框架插入时设置 Id 字段【英文标题】:Set Id field when inserting using entity framework 【发布时间】:2020-06-16 11:45:07 【问题描述】:

尝试插入实体时出现错误,(整个图是正确的)当 Id 具有一些设定值时使用实体框架,例如 6。 它之所以拥有它是因为它存储在另一个数据库中,我可以为列表中的每个元素以及子列表中的每个元素等手动设置它。但我必须为每个元素和每个查询执行此操作。 我希望有独特的全局方式将 Id 字段设置为零,因为当我尝试插入值时出现错误:

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

我希望我的数据库设置 Id,如果 Id 设置为除 0 以外的任何值,则使实体框架忽略

我尝试的是 ovveride OnSaveChanges,但这似乎已经晚了,因为当使用 Add 或 AddRange 方法时,对象开始被跟踪,我无法将 Id 设置为列表中的多个对象。

【问题讨论】:

在将Id 添加到任何实体框架集合/跟踪之前,您能否在业务逻辑中将其设置为 0?听起来您是从另一个系统中获取对象,该系统已经具有标识符并且需要去除该标识符,我将其解释为两个系统之间映射的业务逻辑。 当然,但是我必须对列表中的每个元素进行 foreach,如果每个元素都有子列表,我必须对该子列表进行 foreach。如果子列表的每个元素都有一些复杂的对象,我必须设置它。此外,我必须在很多地方的每个查询中执行此操作。这样做没有意义,而是要找到“全局”的方法 【参考方案1】:

在 EF Core 3.x 中,您可以处理 ChangeTracker.Tracked 事件并为每个 Added 实体显式重置设置自动生成的 PK(它不需要在外部,您可以将 db 上下文自行附加到该事件):


dbContext.ChangeTracker.Tracked += (sender, e) =>

    if (!e.FromQuery && e.Entry.State == EntityState.Added && e.Entry.IsKeySet)
    
        var pk = e.Entry.Metadata.FindPrimaryKey();
        if (pk.Properties.Count == 1)
        
            var pkProperty = pk.Properties[0];
            if (pkProperty.ValueGenerated == ValueGenerated.OnAdd &&
                defaultValues.TryGetValue(pkProperty.ClrType, out var defaultValue))
            
                e.Entry.State = EntityState.Detached;
                e.Entry.CurrentValues[pkProperty] = defaultValue;
                var newEntry = e.Entry.Context.Entry(e.Entry.Entity);
                newEntry.State = EntityState.Added;
            
        
    
;

其中defaultValues 是一个字典,其中包含支持的自动增量属性类型的盒装0(零)值(如果需要,您可以添加更多):

static readonly Dictionary<Type, object> defaultValues = new Dictionary<Type, object>

     typeof(int), default(int) ,
     typeof(long), default(long) ,
     typeof(decimal), default(decimal) ,
;

但请注意,这不会修复使用显式 FK 属性而没有引用导航属性的关系。

【讨论】:

我会尝试,但它似乎会工作。能否请您也看看这个问题:***.com/questions/62425113/… 不幸的是,当我尝试插入另一个对象时,它恰好有 Id 1,但在上下文中已经有另一个 Id=1 我得到错误:实体类型“车辆”的实例不能被跟踪,因为另一个具有键值“Id: 1”的实例已经被跟踪。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。在我有机会将该值清除为零之前 是的,不幸的是,这不适用于将 AddNew 与其他其他 CRUD 混合使用。 跟踪之前没有扩展点。 是的,但我可以在使用此组合时使用 AsNoTracking 禁用跟踪。你有没有机会看看这个有趣的问题:***.com/questions/62425113/… 还没有,有时间会的。

以上是关于使用实体框架插入时设置 Id 字段的主要内容,如果未能解决你的问题,请参考以下文章

实体框架:多对多插入重复

使用自定义 ID 插入数据的 Code-First 实体框架

违反 PRIMARY KEY 约束“[Table_id]”。使用实体框架时无法在对象“[Table_id]”中插入重复键

如何阻止实体框架尝试保存/插入子对象?

如何强制实体框架插入标识列?

在实体框架数据库优先方法中将默认值 1 设置为实体名称(状态)