如何在模型中保留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
相关产品推荐
相关产品推荐

