2

J'ai une table composée d'une colonne de nombres pré-remplis. Mon API utilisant Nhibernate saisit les 10 premières lignes où le drapeau 'Utilisé' est défini comme faux. Quel serait le meilleur moyen d'éviter le problème de simultanéité lorsque plusieurs sessions essaient de récupérer une ligne de la table? Après avoir sélectionné la ligne, je peux mettre à jour la colonne des indicateurs pour qu'elle soit vraie afin que les appels suivants n'utilisent pas les mêmes numéros.Nhibernate Concurrence entre plusieurs sessions

+0

Il y a plusieurs façons d'y parvenir; c'est pourquoi, la question est basée sur l'opinion. En outre, la question a très peu à voir avec NHibernate. –

Répondre

0

Avec un tel contexte général, il pourrait se faire de cette façon:

// RepeatableRead ensures the read rows does not get concurrently updated by another 
// session. 
using (var tran = session.BeginTransaction(IsolationLevel.RepeatableRead)) 
{ 
    var entities = session.Query<Entity>() 
     .Where(e => !e.Used) 
     .OrderBy(e => e.Id) 
     .Take(10) 
     .ToList(); 
    foreach(var entity in entities) 
    { 
     e.Used = true; 
    } 
    // If your session flush mode is not the default one and does not cause 
    // commits to flush the session, add a session.Flush(); call before committing. 
    tran.Commit(); 
    return entities; 
} 

Il est simple. Il peut échouer avec un blocage, auquel cas vous devrez jeter la session, en obtenir une nouvelle et réessayer. L'utilisation d'un modèle de mise à jour optimiste peut être une solution alternative, mais cela nécessite également du code pour la récupération à partir de tentatives échouées.

En utilisant une solution sans verrouillage explicite, ce qui ne causera pas des risques de blocage, pourrait le faire, mais il faudra plus de requêtes:

const int entitiesToObtain = 10; 
// Could initialize here with null instead, but then, will have to check 
// for null after the while too. 
var obtainedEntities = new List<Entity>(); 
while (obtainedEntities.Count == 0) 
{ 
    List<Entity> candidates; 
    using (var tran = session.BeginTransaction()) 
    { 
     candidatesIds = session.Query<Entity>() 
      .Where(e => !e.Used) 
      .Select(e => e.Id) 
      .OrderBy(id => id) 
      .Take(entitiesToObtain) 
      .ToArray(); 
    } 
    if (candidatesIds.Count == 0) 
     // No available entities. 
     break; 

    using (var tran = session.BeginTransaction()) 
    { 
     var updatedCount = session.CreateQuery(
      @"update Entity e set e.Used = true 
       where e.Used = false 
        and e.Id in (:ids)") 
      .SetParameterList("ids", candidatesIds) 
      .ExecuteUpdate(); 
     if (updatedCount == candidatesIds.Length) 
     { 
      // All good, get them. 
      obtainedEntities = session.Query<Entity>() 
       .Where(e => candidatesIds.Contains(e.Id)) 
       .ToList(); 
      tran.Commit(); 
     } 
     else 
     { 
      // Some or all of them were no more available, and there 
      // are no reliable way to know which ones, so just try again. 
      tran.Rollback(); 
     } 
    } 
} 

Il utilise NHibernate DML-style operations comme suggéré here. Un strongly typed alternative est disponible dans NHibernate v5.0.