2

미리 채워진 숫자의 열로 구성된 표가 있습니다. Nhibernate를 사용하는 API는 'Used'플래그가 false로 설정된 처음 10 개의 행을 가져옵니다. 여러 세션이 테이블에서 행을 가져 오려고 할 때 동시성 문제를 피할 수있는 최선의 방법은 무엇입니까? 행을 선택한 후 플래그 열을 True로 업데이트하여 후속 호출이 동일한 번호를 사용하지 않도록 할 수 있습니다.Nhibernate 여러 세션 간의 동시성

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

그것은 간단하다 : 이러한 일반적인 문맥으로

+0

이렇게하려면 여러 가지 방법이 있습니다. 그 이유는 질문이 의견을 기반으로하기 때문입니다. 또한, 질문은 NHibernate와는 거의 관계가 없다. –

답변

0

, 그것은 그런 식으로 일을 할 수있다. 교착 상태로 실패 할 수 있습니다.이 경우 세션을 버리고 새 세션을 만들어 다시 시도해야합니다.

낙관적 인 업데이트 패턴을 사용하는 것이 대안 일 수 있지만 실패한 시도에서도 복구 할 수있는 코드가 필요합니다.

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

here을 제안 NHibernate DML-style operations를 사용, 교착 상태의 위험을 발생하지 않습니다 그것을 할 수 있지만, 더 많은 쿼리를 필요로하는 명시적인 잠금 솔루션을 사용

. strongly typed alternative은 NHibernate v5.0에서 사용할 수 있습니다.