/// <summary> /// Uruchomienie na bazie pewnej porcji pracy (NHUnitOfWork) /// </summary> /// <remarks> /// Metoda domyślnie może być wywołana tylko z parametrem work, wtedy zostanie automatycznie założona /// sesja i trnsakacja. /// Dodatkowo aby ułatwić testy można podać jako parametr własną sesję. Jeżeli zostanie podana własna sesja /// to w zależności czy jest w niej transakcja (raczej powinna już być) zostanie założona lub nie. /// /// </remarks> /// <param name="work">Zadania do wykonania</param> /// <param name="session">Opcjonalna sesja z trnskcją, jeżeli nie chcemy aby metoda założyła własną</param> public void ExecuteWork(IUnitOfWork work, ISession session = null) { INHUnitOfWork nhWork = work as INHUnitOfWork; if (nhWork == null) { throw new ArgumentException("Zadanie musi implementować INHUnitOfWork", "work"); } //licznik deadlocków, jak za dużo to się poddaję int deadlockCnt = 0; //skrót do konfiguracji Properties.Settings cfg = Properties.Settings.Default; //próbuję wykonać pracę, jeżęli wystąpi deadlock i jest to moja sesja... to //staram się ponowić N-razy pracę while (true) { try { TryExecuteWork(nhWork, session); return; } catch (GenericADOException e) { if (CurrenctDialectHelper.IsDeadlock(e) && session == null && deadlockCnt < cfg.MaxInterceptedDeadlocks) { Log.Warn("Deadlock intercepted: ", e); //1. Wystapił deadlock... //2. Jest to moja sesja więc mogę założyć ją od nowa, oraz transakcję //3. Nie wyczerpałem limitu przechwyceń... Random _r = new Random(); //usypiam na losowy czas, nie chcę 'atakować' bazy w pętli System.Threading.Thread.Sleep(_r.Next(cfg.MinDeadlockSleep, cfg.MaxDeadlockSleep)); deadlockCnt++; } else { Log.Error(e.Message, e); //Nie jest to deadlock, nie jest to moja sesja.. lub było ich za dużo throw; } } } }
/// <summary> /// Metoda wewnętrzna, wywoływana w pętli na wypadek gdyby wystąpił deadlock /// Parametry identyczne jak dla ExecuteWork /// </summary> protected virtual void TryExecuteWork(INHUnitOfWork work, ISession session = null) { if (work == null) { throw new ArgumentNullException("work"); } ISession s; bool mySession = session == null; if (mySession) { s = SessionFactory.OpenSession(); } else { s = session; } ITransaction t = null; bool myTran = mySession || s.Transaction == null; if (myTran) { if ((int)work.TransactionIsolationLevel == 0) { work.TransactionIsolationLevel = System.Data.IsolationLevel.ReadCommitted; } t = s.BeginTransaction(work.TransactionIsolationLevel); } try { try { work.Session = s; work.Execute(); if (myTran) //jak moja to robię commit { try { t.Commit(); } catch (GenericADOException e) { if (e.InnerException != null) { Log.Error("Commit exception", e.InnerException); } Log.Error("Commit exception", e); throw; } } } catch (Exception e) { if (myTran) { try { //jeżeli to nie deadlock.. to mam problem if (!CurrenctDialectHelper.IsDeadlock(e)) { t.Rollback(); } } catch (GenericADOException e_rollback) { if (e_rollback.InnerException != null) { Log.Error("Rollback exception", e_rollback.InnerException); } Log.Error("Rollback exception", e_rollback); throw; } } catch (TransactionException e_rollback) { Log.Error("Rollback exception", e_rollback); throw; } //jeżeli był deadlock, to obsługa jego jest metodzie wywołującej tą throw; } }