You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Audit.EntityFramework触发SqlException:无法插入标识列值

问题描述

环境

  • Entity Framework Core
  • .NET 7
  • SQL Server

错误信息

Cannot insert explicit value for identity column in table 'AuditLogs' when IDENTITY_INSERT is set to OFF.
中文翻译:当IDENTITY_INSERT设置为OFF时,无法为'AuditLogs'表中的标识列插入显式值。

相关信息

  • 已开启IDENTITY_INSERT
  • 列配置为Identity(1,1)主键
  • 首次实现该功能,可能存在遗漏点

配置代码

Program.cs 配置

private static void AuditSetup()
{
    // Set up Audit.NET
    Audit.Core.Configuration.DataProvider = new EntityFrameworkDataProvider()
    {
        DbContextBuilder = ev => new DatabaseContext(AppConfig.ConnectionString),
        AuditTypeMapper = (t, ee) =>
        {
            var typesToAudit = new List<Type>()
            {
                typeof(LiveLoadLocking)
            };

            if (typesToAudit.Contains(t))
            {
                return typeof(AuditLog);
            }
            return null;
        },
        AuditEntityAction = async (evt, entry, auditEntity) =>
        {
            AuditLog a = (AuditLog)auditEntity;

            a.AuditDate = DateTime.UtcNow;
            a.UserName = evt.Environment.UserName;
            a.AuditAction = entry.Action;
            a.EntityType = entry.EntityType.Name;
            a.EntityId = entry.PrimaryKey.FirstOrDefault().Value?.ToString();

            var changes = entry.Changes;

            if (changes != null)
            {
                a.ChangedColumns = string.Join(", ", changes.Select(c => c.ColumnName));
                a.OldValues = JsonConvert.SerializeObject(changes.ToDictionary(c => c.ColumnName, c => c.OriginalValue));
                a.NewValues = JsonConvert.SerializeObject(changes.ToDictionary(c => c.ColumnName, c => c.NewValue));
            }

            return true;
        }
    };
}

AuditLog 实体类

public class AuditLog
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public DateTime AuditDate { get; set; }
    public string UserName { get; set; }
    public string AuditAction { get; set; }
    public string EntityType { get; set; }
    public string EntityId { get; set; }
    public string OldValues { get; set; }
    public string NewValues { get; set; }
    public string ChangedColumns { get; set; }
}

modelBuilder 配置

modelBuilder.Entity<AuditLog>(entity =>
{
    entity.HasKey(e => e.Id).HasName("PK__AuditLog__somename");
    entity.Property(e => e.Id).ValueGeneratedOnAdd();
    entity.Property(e => e.AuditDate).IsRequired();
    entity.Property(e => e.UserName).HasMaxLength(256);
    entity.Property(e => e.AuditAction).IsRequired().HasMaxLength(50);
    entity.Property(e => e.EntityType).IsRequired().HasMaxLength(128);
    entity.Property(e => e.EntityId).IsRequired().HasMaxLength(128);
    entity.Property(e => e.OldValues).HasColumnType("nvarchar(max)");
    entity.Property(e => e.NewValues).HasColumnType("nvarchar(max)");
    entity.Property(e => e.ChangedColumns).HasColumnType("nvarchar(max)");
});

上下文类结构

public partial class DFDBContext : Audit.EntityFramework.AuditDbContext
public partial class DatabaseContext : DFDBContext

保存逻辑示例

public static void UnlockLoad(string df)
{
    using (var context = new DatabaseContext(AppConfig.ConnectionString))
    {
        LiveLoadLocking loadLock = context.LiveLoadLockings
            .SingleOrDefault(x => x.somefield == somenumber);

        loadLock.IsLocked = false;
        loadLock.LockedBy = "";
        loadLock.LastUpdated = DateTime.Now;

        if (context.SaveChanges() <= 0) { throw new ContextFailedToSaveException("Items weren't saved or updated to the database.", loadLock); }
    }
}

疑问

此前其他表使用相同标识列配置从未出现问题,推测是Audit组件尝试自行插入AuditLog表的标识列值。请问有何解决办法或如何阻止该行为?


解决方案

1. 强制重置标识列值

在AuditEntityAction中,获取AuditLog实例后立即重置Id为0,避免Audit组件自动赋值导致EF Core认为要插入显式值:

AuditLog a = (AuditLog)auditEntity;
a.Id = 0; // 让EF Core自动生成标识列值
// 后续属性赋值代码保持不变

2. 统一标识列配置方式

实体类和modelBuilder的标识列配置重复,可能导致EF Core识别冲突,任选一种配置方式即可:

  • 移除实体类上的[DatabaseGenerated(DatabaseGeneratedOption.Identity)],保留modelBuilder中的ValueGeneratedOnAdd();
  • 或者移除modelBuilder中的ValueGeneratedOnAdd(),保留实体类的特性标记。

3. 明确实体状态

在AuditEntityAction中手动将AuditLog实体标记为新增状态,确保EF Core正确处理标识列:

AuditEntityAction = async (evt, entry, auditEntity) =>
{
    AuditLog a = (AuditLog)auditEntity;
    var context = entry.Context as DatabaseContext;

    a.Id = 0;
    a.AuditDate = DateTime.UtcNow;
    // 其他属性赋值...

    context.Entry(a).State = EntityState.Added;
    return true;
};

4. 关闭IDENTITY_INSERT

既然希望数据库自动生成标识列值,无需开启IDENTITY_INSERT,反而可能因为开启它导致EF Core强制要求提供显式Id值。直接关闭该设置,让EF Core按照正常的标识列逻辑处理即可。


内容的提问来源于stack exchange,提问作者Whaaa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 13:30:17