乐观并发处理 - 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。
- 如何使用 etag 检查并发性?
- 是否可以实现并发检查的自定义属性?
类似于:
[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 以便可以重用。
这是我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。
- 如何使用 etag 检查并发性?
- 是否可以实现并发检查的自定义属性?
类似于:
[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 以便可以重用。