实体框架-未强制执行EF核心并发



我遵循了这里的文档,我使用的是SQL Server 2016(https://ef.readthedocs.io/en/latest/modeling/concurrency.html)

我在表中添加了一个名为VersionCol的时间戳列,然后当我运行Scaffold DbContext时,它会在我的DbContext中的表实体上放置以下属性。

entity.Property(e => e.VersionCol)
    .IsRequired()
    .HasColumnType("timestamp")
    .ValueGeneratedOnAddOrUpdate()

它缺少.IsConcurrencyToken(),所以我自己添加了它,但在应该遇到并发问题的情况下仍然不会抛出异常。相反,它只是简单地重写数据。

我有什么东西不见了吗?

编辑:

我使用数据库优先的方法(因此没有[Timestamp]或任何其他注释),我的DbContext被注入到一个服务中,在Startup.cs 中配置services.AddScoped<IPoRepository, PoRepository>()

它在我的模型中生成了一个public byte[] VersionCol { get; set; }字段,我相信这是正确的。

在我的PoRepository中,我试图用以下内容更新我的Po:

public void SavePo(PoListing poListing) {
    Po po;
    try {
        po = _context.Po.Where( p => p.Poid == poListing.PoId ).First();
    } catch ( ArgumentNullException ) {
        throw new ArgumentNullException( "The PO does not exist." );
    }
    po.AssignedUserId = poListing.AssignedUserId;
    po.VersionCol     = poListing.VersionCol;
    _context.Entry( po ).State = EntityState.Modified;
    _context.SaveChanges();
}

PoListing本质上只是Po的一部分,所以它只有它的一些列(它不是数据库中的表),并且它在第一次生成时具有Po的VersionCol。如果PoListing的VersionCol比它所基于的Po旧,那么它应该给出一个异常。

第2版:

这是可行的,但我不知道如何在不需要创建第二个上下文的情况下使其工作,只需要使用注入的上下文。

public void SavePo(PoListing poListing) {
    DbContextOptionsBuilder<TMS_1000Context> options = new DbContextOptionsBuilder<TMS_1000Context>();
    options.UseSqlServer( "Server=DEVSQL16;Database=TMS_1000_Dev;Trusted_Connection=True;MultipleActiveResultSets=true" );
    TMS_1000Context context1;
    try {
        po = _context.Po.Where( p => p.Poid == poListing.PoId ).First();
    } catch ( ArgumentNullException ) {
        throw new ArgumentNullException( "The PO does not exist." );
    }
    using ( context1 = new TMS_1000Context( options.Options ) ) {
        po.AssignedUserId = poListing.AssignedUserId;
        po.VersionCol = poListing.VersionCol;
        context1.Update( po );
        context1.SaveChanges();
    }
}

第3版:

这目前正在工作。还有别的办法吗?

public void SavePo(PoListing poListing) {
    Po po;
    try {
        po = _context.Po.Where( p => p.Poid == poListing.PoId ).First();
    } catch ( ArgumentNullException ) {
        throw new ArgumentNullException( "The PO does not exist." );
    }
    po.AssignedUserId = poListing.AssignedUserId;
    _context.Entry( po ).Property( u => u.VersionCol ).OriginalValue = poListing.VersionCol;
    _context.Update( po );
    _context.SaveChanges();
}

我之所以相信会发生这种情况,是因为EF Core跟踪只关心原始值是否与数据库中当前的值相同,如果它们不相同,则会引发并发异常。

以下是我发现的3个修复程序。

  1. 更改原始值,使其与数据库中存在的值不同。

    public void SavePo(PoListing poListing) {
        Po po;
        try {
            po = _context.Po.Where( p => p.Poid == poListing.PoId ).First();
        } catch ( ArgumentNullException ) {
            throw new ArgumentNullException( "The PO does not exist." );
        }
        po.AssignedUserId = poListing.AssignedUserId;
        _context.Entry( po ).Property( u => u.VersionCol ).OriginalValue = poListing.VersionCol;
        _context.Update( po );
        _context.SaveChanges();
    }
    
  2. 使用AsNoTracking()获取实体。现在EF Core将不再只是比较原始值。

    public void SavePo(PoListing poListing) {
        Po po;
        try {
            po = _context.Po.AsNoTracking().Where( p => p.Poid == poListing.PoId ).First();
        } catch ( ArgumentNullException ) {
            throw new ArgumentNullException( "The PO does not exist." );
        }
        po.AssignedUserId = poListing.AssignedUserId;
        po.VersionCol     = poListing.VersionCol;
        _context.Update( po );
        _context.SaveChanges();
    }
    
  3. 从上下文中分离实体。与修复#2 功能相似

    public void SavePo(PoListing poListing) {
        Po po;
        try {
            po = _context.Po.Where( p => p.Poid == poListing.PoId ).First();
        } catch ( ArgumentNullException ) {
            throw new ArgumentNullException( "The PO does not exist." );
        }
        po.AssignedUserId = poListing.AssignedUserId;
        po.VersionCol     = poListing.VersionCol;
        _context.Entry( po ).State = EntityState.Detached;
        _context.Update( po );
        _context.SaveChanges();
    }
    

最新更新