简体   繁体   English

EF Core Concurrency未被强制执行

[英]EF Core Concurrency not being enforced

I've followed the documentation here, and I'm using SQL Server 2016.( https://ef.readthedocs.io/en/latest/modeling/concurrency.html ) 我已经按照这里的文档,我正在使用SQL Server 2016.( https://ef.readthedocs.io/en/latest/modeling/concurrency.html

I've added a timestamp column to my table named VersionCol, and then when I run Scaffold-DbContext it places the following property on my table entity in my DbContext. 我在名为VersionCol的表中添加了一个timestamp列,然后当我运行Scaffold-DbContext时,它将以下属性放在我的DbContext中的表实体上。

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

It's missing .IsConcurrencyToken() so I've added that on myself, but still no exceptions are thrown in situations that should suffer concurrency issues. 它缺少.IsConcurrencyToken()所以我在自己身上添加了这个,但是在应该遇到并发问题的情况下仍然没有异常。 It instead simply over-writes the data. 它只是简单地重写数据。

Is there something I am missing? 有什么我想念的吗?

Edit: 编辑:

I'm using a Database-first approach (so no [Timestamp] or any other annotations), and my DbContext is being injected into a service, configured with services.AddScoped<IPoRepository, PoRepository>() in Startup.cs 我正在使用数据库优先方法(所以没有[Timestamp]或任何其他注释),我的DbContext被注入到一个服务中,在Startup.cs中配置了services.AddScoped<IPoRepository, PoRepository>()

It generates a public byte[] VersionCol { get; set; } 它生成一个public byte[] VersionCol { get; set; } public byte[] VersionCol { get; set; } public byte[] VersionCol { get; set; } field in my model, which is right I believe. public byte[] VersionCol { get; set; }在我的模型,这是对的,我相信场。

In my PoRepository I'm trying to update my Po with the following: 在我的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();
}

The PoListing is essentially just a part of a Po, so It only has some of it's columns (it's not a table in the database), and it has the VersionCol of the Po when it is first generated. PoListing本质上只是Po的一部分,因此它只有一些列(它不是数据库中的表),并且它在首次生成时具有Po的VersionCol。 If the PoListing has an older VersionCol than the Po it's based off, then it should give an exception. 如果PoListing的VersionCol比它所基于的Po更旧,那么它应该给出一个例外。

Edit2: EDIT2:

This works, but I can't figure out how to make it work without needing to make this second context, and just use the injected context. 这是有效的,但我无法弄清楚如何让它工作而不需要创建第二个上下文,只需使用注入的上下文。

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: EDIT3:

This is currently working. 这目前正在运作。 Is there another way? 还有另外一种方法吗?

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

The reason I believe this is happening is because EF Core tracking cares only if the original values are the same as what's currently in the database, and if they aren't then that's when a concurrency exception is thrown. 我认为这种情况发生的原因是因为EF Core跟踪仅在原始值与数据库中当前值相同时才会关注,如果不是,则抛出并发异常时就是这样。

Here are 3 fixes I've found. 以下是我发现的3个修复程序。

  1. Change the original value so that it is now different than what exists in the database. 更改原始值,使其现在与数据库中存在的值不同。

     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. Get the entity with AsNoTracking(). 使用AsNoTracking()获取实体。 Now EF Core won't just compare the original values. 现在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. Detach the entity from the context. 从上下文中分离实体。 Similar function as fix #2 与修复#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(); } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM