EF Core Concurrency未被强制执行

时间:2016-07-26 23:28:45

标签: entity-framework asp.net-core entity-framework-core

我已经按照此处的文档进行操作,并且我正在使用SQL Server 2016。(https://ef.readthedocs.io/en/latest/modeling/concurrency.html

我在名为VersionCol的表中添加了一个timestamp列,然后当我运行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更旧,那么它应该是例外。

EDIT2:

这很有效,但我无法弄清楚如何使其工作而不需要创建第二个上下文,只需使用注入的上下文。

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();
    }
}

EDIT3:

目前正在运作。还有另一种方式吗?

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();
}

1 个答案:

答案 0 :(得分:0)

我认为发生这种情况的原因是因为只有当原始值与数据库中当前的值相同时,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();
    }