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

如何在模型中保留Concurrency Token RowVersion但不映射到数据库?

问题描述

希望在实体模型中添加名为RowVersion的并发令牌字段,用于避免更新操作时的并发冲突,但在将模型迁移到数据库时,不生成该字段对应的数据库列。

以下是示例实体模型代码:

public class EntityModelBase : IEntityModel
{
    public EntityModelBase()
    {
    }

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Display(Name = "شناسه سیستمی"), Column("Id", Order = -1)]
    public virtual int Id { get; set; }

    [Required]
    [DefaultValue(0)]
    public virtual bool IsDeleted { get; set; }

    [NotMapped]
    [JsonIgnore]
    [ConcurrencyCheck]
    [Timestamp]
    [Column("RV", TypeName = "rowversion", Order = byte.MaxValue)]
    public virtual byte[]? RowVersion { get; } = new Guid().ToByteArray();
}

尝试在RowVersion属性上添加NotMapped特性,确实避免了在数据库中创建对应列,但Entity Framework每次返回该字段的值都是NULL。请问如何正确配置,既能让EF支持该并发令牌,又不在数据库中生成对应的列?

解决方案

核心矛盾在于:NotMapped会让EF完全忽略该属性,无法跟踪并发;但直接用Timestamp或ConcurrencyCheck又会触发EF生成数据库列。要解决这个问题,需通过Fluent API手动配置,同时依赖数据库中已存在的rowversion类型列(注意:数据库必须提前有RV列,否则并发检查无法生效)。

步骤1:修改实体类

移除冲突特性,将属性改为可设置(EF需要在查询后赋值):

public class EntityModelBase : IEntityModel
{
    public EntityModelBase()
    {
    }

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Display(Name = "شناسه سیستمی"), Column("Id", Order = -1)]
    public virtual int Id { get; set; }

    [Required]
    [DefaultValue(0)]
    public virtual bool IsDeleted { get; set; }

    [JsonIgnore]
    public virtual byte[]? RowVersion { get; set; }
}

步骤2:通过Fluent API配置并发令牌

在DbContext的OnModelCreating方法中,告诉EF该属性是并发令牌,映射到已有的数据库列,但不参与迁移生成:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    // 针对单个基类实体配置
    modelBuilder.Entity<EntityModelBase>()
        .Property(e => e.RowVersion)
        .HasColumnName("RV")
        .HasColumnType("rowversion")
        .IsConcurrencyToken()
        .ValueGeneratedOnAddOrUpdate();

    // 如果所有继承自EntityModelBase的实体都需要此配置,可批量处理
    foreach (var entityType in modelBuilder.Model.GetEntityTypes()
                 .Where(t => typeof(EntityModelBase).IsAssignableFrom(t.ClrType)))
    {
        modelBuilder.Entity(entityType.ClrType)
            .Property("RowVersion")
            .HasColumnName("RV")
            .HasColumnType("rowversion")
            .IsConcurrencyToken()
            .ValueGeneratedOnAddOrUpdate();
    }
}

关键说明

  • 数据库必须提前手动创建RV列(类型为rowversion/timestamp),因为我们不让EF迁移生成它。现有数据库需确认列存在;新数据库需手动在迁移脚本中添加该列,再删除EF自动生成的RowVersion相关代码。
  • IsConcurrencyToken()让EF在更新/删除时检查该值,ValueGeneratedOnAddOrUpdate()让EF自动从数据库获取最新的rowversion值。
  • 若不想手动建列,可保留实体类的Timestamp和Column特性(移除NotMapped),生成迁移后手动删除创建RV列的代码,但此方法需每次迁移都手动检查,可靠性较低。

内容的提问来源于stack exchange,提问作者Amin Amiri Darban

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 14:30:49