这个东西主要是用来做版本 并发冲突检查的。 跟TimeStamp不一样的地方是我们可以给多个字段加上这个ConcurrencyCheck
在高并发的时候。 比如说我们的产品有一个库存的数据。这个数据我们希望更新的时候是准确的。 就可以使用这个Attribute了。
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
[ConcurrencyCheck]
public int StockQuantity { get; set; }
public DateTime UpdatedOnUtc { get; set; }
}
public void ChangeName()
{
using (var dbContext = this.dbContextFactory.CreateDbContext())
{
var p = dbContext.Products.FirstOrDefault();
p.Name = DateTime.Now.Ticks.ToString();
dbContext.SaveChanges();
}
}
我们可以看到它生成的Sql里面加了一个where. (条件变成了StockQuantity)
SET NOCOUNT ON;
UPDATE [Products] SET [Name] = @p0
WHERE [Id] = @p1 AND [StockQuantity] = @p2;
SELECT @@ROWCOUNT;
假设我们默认的 prodcutQuantity是0. 运行这个之后的数值会是 1000吗。 答案是不会。
public void Run()
{
var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
var task = Task.Run(() =>
{
for (int j = 0; j < 100; j++)
{
using (var dbContext = this.dbContextFactory.CreateDbContext())
{
var p = dbContext.Products.FirstOrDefault();
p.StockQuantity += 1;
dbContext.SaveChanges();
}
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
我们会得到一个并发冲突的导常。
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException:“Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded.
如何处理这个异常保证我们的数据可以正常的更新呢。 比较简单的机制就是加入重试的机制。
public void Run()
{
var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
var task = Task.Run(() =>
{
for (int j = 0; j < 100; j++)
{
Retry();
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
private void Retry()
{
//用For循环来保证。
for (int m = 0; m < 1000; m++)
{
try
{
using (var dbContext = this.dbContextFactory.CreateDbContext())
{
var p = dbContext.Products.FirstOrDefault();
p.StockQuantity += 1;
dbContext.SaveChanges();
}
break;
}
catch (DbUpdateConcurrencyException ex)
{
Thread.Sleep(10);
}
}
}
如果冲突确实很多的情况下。这种重试的性能是不太好的。请考虑用锁Lock 上面的代码是类似于CAS操作——Compare & Set
modelBuilder.Entity<Product>().Property(x => x.StockQuantity).IsConcurrencyToken();
上面的完整代码可以在分支concurrency/concurrency-check看到