乐观并发处理 - Asp.Net WebApi Odata V4

Optimistic Concurrency Handling - Asp.Net WebApi Odata V4

这是我OdataController

Patch方法
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Product> patch)
{
   Validate(patch.GetInstance());
   Product product = await service.Products.GetAsync(key);
   if (product == null)
       return NotFound();

   patch.Put(product);

   try
   {
       await service.Products.UpdateAsync(product);
   }
   catch (DbUpdateConcurrencyException)
   {
       if (!await service.Products.ExistAsync(key))
           return NotFound();
       else
           throw;
   }

   return Updated(product);
}

我的模型有 属性:

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

DbUpdateConcurrencyException似乎根本不起作用。 我需要使用 Etag 实现并发检查机制。 我看过一些例子 here。但是他们没有在方法中使用 Delta。

  1. 如何使用 etag 检查并发性?
  2. 是否可以实现并发检查的自定义属性?

类似于:

[CustomConcurrencyCheck]
public async Task<IHttpActionResult> Put([FromODataUri] int key, Delta<Product> patch)
{
...
}

提供一个简单的例子将不胜感激。

首先在 WebApiConfig 创建模型时,您必须指定哪个 属性 是 ETag,在您的情况下:

var builder = new ODataConventionModelBuilder();
builder.EntityType<Product>()
    .Property(p => p.RowVersion)
    .IsConcurrencyToken();

稍后您可以从控制器中 Patch 方法的 ODataQueryOptions<T> 参数中检索 ETag:

[AcceptVerbs("PATCH", "MERGE")]
public IHttpActionResult Patch([FromODataUri] int key, Delta<Product> delta, ODataQueryOptions<Product> options) {
    var existingEntity = //Code to get existing entity by ID, 404 if not found

    byte[] requestETag = options.IfMatch["RowVersion"] as byte[];
    if(!requestETag.SequanceEqual(existingEntity.RowVersion)) { //Simplified if-statement, also do null-checks and such
        // ETags don't match, return HTTP 412
        return StatusCode(HttpStatusCode.PreconditionFailed);
    } else {
        // The ETags match, implement code here to update your entity
        // You can use the 'Delta<Product> delta' parameter to get the changes and use the 'Validate' function here
        // ...

这是我使用的解决方案,它是一个简单的检查,以查看请求更新的客户端是否具有与服务具有的 object 相同的版本。我的解决方案的一个显着缺点是我必须从数据库中检索现有的 object 才能使其正常工作,这会降低一些性能。

这是 If-Match header 的代码,ODataQueryOptions<T> 也有 .IfNoneMatch[VersionColumnName] 可用。您可以在 Get 方法中使用它。如果 If-None-Match header 等于您的 RowVersion 您可以 return 一个 HTTP 304 (Not modified) 并节省一些带宽。

这是一个非常简单的示例,如果您想实现您自己的自定义属性,这取决于您。至少我会将其中的一些逻辑移至助手 class 以便可以重用。