【问题标题】:Why RowVersion property is not raising optimistic concurrency exception when two concurrent changes occur?当两个并发更改发生时,为什么 RowVersion 属性不会引发乐观并发异常?
【发布时间】:2019-01-17 12:48:51
【问题描述】:

我有以下实体,

 public class PatientRegistry : BaseEntity {

        [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Display(Name = "Patient File Number")]
        public long PatientFileId { get; set; }
        public virtual ICollection<PartnerRegistry> Partners { get; set; }
        public string UserId { get; set; }
        public string AliasName { get; set; }

        public DateTime? DateNow { get; set; }
        public virtual ApplicationUser User { get; set; }

    }

BaseEntity 是,

 public class BaseEntity : ISoftDelete {
        public bool IsDeleted { get; set; }

        [Timestamp]
        public byte[] RowVersion { get; set; }
    }

实体具有RowVersion 属性,我使用流式API 将其配置为RowVersion。我使用存储库模式,这就是我在上下文中加载实体的方式,

 public async Task<PatientRegistry> GetPatient(long Id, bool isPartners = true, bool isUser = true) {
            if (isPartners && !isUser)
              return await context.PatientsRegistry
                .IgnoreQueryFilters()
                .Include(pt => pt.Partners)
                .Include(pt => pt.User)
                .Include(pt => pt.User.Gender)
                .Include(pt => pt.User.Nationality)
                .Include(pt => pt.User.Occupation)
                .Include(pt => pt.User.Ethnicity)
                .Include(pt => pt.User.MaritalStatus)
                .Include(pt => pt.User.Country)
                .Include(pt => pt.User.State)
                .Include(pt => pt.User.City)
                .Include(pt => pt.User.Database)
                .Include(pt => pt.User.UserAvatar)
                .SingleOrDefaultAsync(pt => pt.PatientFileId == Id);
        }

在我的控制器中,我更新如下,

[HttpPut("{FileId}")]
public async Task<IActionResult> UpdatePatient([FromRoute] PatientFileIdResource fileIdModel, [FromBody] SavePatientsRegistryResource model)
{
    ApiSuccessResponder<PatientRegistryResource> response = new ApiSuccessResponder<PatientRegistryResource>();
    if (!ModelState.IsValid)
        return BadRequest(ModelState);
    Task<PatientRegistry> getPatientTask = repository.GetPatient(fileIdModel.FileId.Value);
    Task<RequestHeaderResource> getHeaderRequestTask = headerRequestService.GetUserHeaderData();
    await Task.WhenAll(getPatientTask, getHeaderRequestTask);
    PatientRegistry patient = await getPatientTask.ConfigureAwait(false);
    RequestHeaderResource header = await getHeaderRequestTask.ConfigureAwait(false);
    if (patient == null)
    {
        ModelState.AddModelError(string.Empty, "The patient does not exist in the database");
        return BadRequest(ModelState);
    }

    patient.DateNow = DateTime.Now;
    patient.AliasName = model.AliasName;
    await unitOfWork.CompleteAsync(header);
    return StatusCode(200, response);
}

现在,如果我使用两个不同的用户和其中一个更改 AliasName 访问同一记录并保存访问该记录的第二个用户,我仍然能够保存新的更改,而不会出现并发异常。为什么这里的并发更新没有引发更新异常?

这是 Fluent API 配置,

 builder.Entity<PatientRegistry>()
        .Property(a => a.RowVersion).IsRowVersion()
        .IsConcurrencyToken()
        .ValueGeneratedOnAddOrUpdate();

我有更新日志here

更新

供日后参考,

根据文档here,我们可以通过在调用 save 之前操作其中一个值来测试并发冲突。

context.Database.ExecuteSqlCommand("UPDATE dbo.PatientsRegistry SET AliasName = 'Jane' WHERE PatientFileId = 2222");
await context.SaveChangesAsync();

这会引发异常。

【问题讨论】:

  • 不,他们没有,但 EF 不应该告诉第二个用户的 RowVersion 低于新用户并拒绝更改吗?
  • 你是对的,我对它的工作原理有一个误解,我一定误解了here

标签: c# entity-framework-core optimistic-concurrency


【解决方案1】:

如果第二个用户的读取第一个用户的写入之后,那么第二个用户的RowVersion 将反映当时的值(即第一次更新之后)。因此 - 没有并发问题。

只有当他们都读取(因此得到相同的RowVersion)并且然后都尝试和写入时,您才会遇到问题。

【讨论】:

    猜你喜欢
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多