0

Dies ist die Patch Methode meiner OdataControlleroptimistisch Concurrency Handhabung - Asp.Net WebAPI OData V4

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

Mein Modell hat eine Eigenschaft:

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

die DbUpdateConcurrencyException überhaupt nicht scheint zu arbeiten. Ich muss Parallelität Prüfmechanismus mit Etag implementieren. Ich habe einige Beispiele gesehen here. Aber sie verwenden nicht Delta in dort Methode.

  1. Wie kann ich Parallelität mit Etags überprüfen?
  2. Ist es möglich, ein benutzerdefiniertes Attribut für Concurrency Cheacking zu implementieren?

etwas wie:

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

ein einfaches Beispiel Providing sehr geschätzt wird.

+0

Wenn Sie ein Delta verwenden, erhalten Sie nur die Eigenschaften, die geändert wurden. ETag oder RowVersion waren Eigenschaften, aber hast du sie geändert? Natürlich nicht. Dann sind beide nicht in Delta-Daten. Wenn Sie die vorherigen Werte von ETag oder RowVersion nicht hatten, wie werden Sie sie vergleichen? –

+0

An dem aktuellen Punkt wird nur eine DbUpdateConcurrencyException angezeigt, wenn zwei (oder mehr) Put-Aktionen für den gleichen Schlüssel zur gleichen Zeit verarbeitet wurden. –

Antwort

1

Zuerst in WebApiConfig während Ihr Modell Erstellen Sie müssen angeben, welche Eigenschaft ist der ETag, in Ihrem Fall:

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

Später können Sie den ETag aus dem ODataQueryOptions<T> Parameter Ihrer Patch Methode in der Steuerung abgerufen werden:

[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 
     // ... 

Dies ist die Lösung, die ich verwende, ist es eine einfache Überprüfung, ob der Client, der die Update-Anfragen zu sehen, ist der Service hat die gleiche Version des Objekts hat. Ein bemerkenswerter Nachteil meiner Lösung besteht darin, dass ich das existierende Objekt aus der DB abrufen muss, um es zum Laufen zu bringen, was etwas Leistung kostet.

Dies ist Code für die If-Match Header, ODataQueryOptions<T> hat auch .IfNoneMatch[VersionColumnName] zur Verfügung. Welche können Sie in Ihrer Get Methode verwenden. Wenn die If-None-Match Kopfzeile gleich RowVersion ist, können Sie eine HTTP 304 (Not modified) zurückgeben und etwas Bandbreite sparen.

Dies ist ein sehr einfaches Beispiel, wenn Sie Ihr eigenes benutzerdefiniertes Attribut implementieren möchten, das Ihnen überlassen ist. Zumindest würde ich etwas von dieser Logik in eine Hilfsklasse verschieben, damit sie wiederverwendet werden kann.

Verwandte Themen