0

Ceci est la méthode Patch de mon OdataControllerOptimiste Gestion de la concurrence - 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); 
} 

Mon modèle a une propriété:

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

le DbUpdateConcurrencyException semble ne fonctionne pas du tout. J'ai besoin d'implémenter un mécanisme de contrôle de concurrence en utilisant Etag. J'ai vu quelques exemples here .Mais ils n'utilisent pas Delta dans la méthode.

  1. Comment puis-je vérifier la concurrence en utilisant etags?
  2. Est-il possible d'implémenter un attribut personnalisé pour le cheacking simultané?

quelque chose comme:

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

Fournir un exemple simple sera très appréciée.

+0

Si vous utilisez un delta, vous n'aurez que les propriétés qui ont été modifiées. ETag ou RowVersion étaient des propriétés mais les avez-vous changées? Bien sûr que non. Ensuite, les deux ne sont pas dans les données delta. Si vous n'avez pas les valeurs précédentes de ETag ou RowVersion, comment les comparerez-vous? –

+0

À ce stade, vous ne verrez qu'une exception DbUpdateConcurrencyException si deux (ou plus) actions Put pour la même clé ont été traitées en même temps. –

Répondre

1

Premier WebApiConfig tout en créant votre modèle, vous devez spécifier quelle propriété ETag, dans votre cas:

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

Plus tard, vous pouvez récupérer le ETag à partir du paramètre ODataQueryOptions<T> de votre méthode Patch dans le contrôleur:

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

C'est la solution que je l'utilise, il est une simple vérification pour voir si le client qui demande la mise à jour a la même version de l'objet le service a. Un inconvénient notable de ma solution est que je dois récupérer l'objet existant de la base de données pour le faire fonctionner, ce qui coûte un peu de performance.

Ceci est le code pour l'en-tête If-Match, ODataQueryOptions<T> a également .IfNoneMatch[VersionColumnName] disponible. Ce que vous pouvez utiliser dans votre méthode Get. Si l'en-tête If-None-Match est égal à RowVersion, vous pouvez renvoyer un HTTP 304 (Not modified) et économiser de la bande passante.

Ceci est un exemple très simple, si vous voulez implémenter votre propre attribut personnalisé à vous. À tout le moins, je déplacerais une partie de cette logique vers une classe d'aide afin qu'elle puisse être réutilisée.