Note that there are some explanatory texts on larger screens.

plurals
  1. PONHibernate, MultiThreading and Shared Object Update Conflict
    primarykey
    data
    text
    <p>I understand that ISession is not thread safe and SessionFactory is thread safe. As such, I've wrapped and confirmed that I have one session per thread.</p> <p>I'm receiving an error under the following situation and was wondering if this is something not supported, or I'm still missing something with my ISession thread isolation.</p> <p>I am running NUnit tests. I have a scenario where my Entity is stubbed out as a field variable. I have a test that runs 2 parallel tasks. </p> <p>• Each parallel task creates its own session from the same SessionFactory and begins an NHibernate Transaction.<br> • They each update the entity and perform a SaveOrUpdate on it.<br> • Then Commit and close the transaction.<br> Each task does this about 10k times.</p> <p>During this test I get a message: </p> <pre><code>System.AggregateException : One or more errors occurred. ----&gt; NHibernate.HibernateException : identifier of an instance of Domain.Entity.MyEntity was altered from 2 to 1 </code></pre> <p>This makes sense because the MyEntity is a field object and consumed by both threads. So a single object created in the NUnit class is reference and updated by both threads. </p> <p>My question is whether a scenario like this can be avoided by pessimistic locking or other NHibernate features? Or is this just not do-able and I have to make sure this situation (i.e my Entity object is not referenced and updated by more than one thread at a time) never occurs in my code? </p> <p>I've tired some options in NHibernate, like ensuring versioning of the entity and tried some locking calls, but I'm guessing in the dark through the documentation which is the right way, if any to handle this scenario.</p> <p>Edit: Thanks for the comments! Here is the code in the unit test:</p> <pre><code>private PluginConfiguration _configStub1; [SetUp] public void Setup() { new FluentMapper().Configuration().ExposeConfiguration( e =&gt; new SchemaExport(e).Drop(false, true) ); _configStub1 = new PluginConfiguration() { Enabled = true, Keys = "Name", Value = "Fred", PluginName = "red", RuntimeId = 1 }; } [Test] [Explicit] public void HighVolume_Saves_MultiManager_SameDataRecord_SameInstance_MultiThread() { Action action1 = () =&gt; { var dal = new DataAccessManager(); for (int i = 0; i &lt; 10000; i++) { dal.Begin(); dal.Current.Session.SaveOrUpdate(_configStub1); dal.Current.Commit(); dal.End(); } }; Action action2 = () =&gt; { var dal = new DataAccessManager(); for (int i = 0; i &lt; 10000; i++) { dal.Begin(); dal.Current.Session.SaveOrUpdate(_configStub1); dal.Current.Commit(); dal.End(); } }; var task1 = Task.Factory.StartNew(action1); var task2 = Task.Factory.StartNew(action2); task1.Wait(); task2.Wait(); } </code></pre> <p>The DataAccess Manager referenced in the test is as follows:</p> <pre><code>public class DataAccessManager : IDataAccessManager { private readonly ThreadLocal&lt;ISessionManager&gt; _current = new ThreadLocal&lt;ISessionManager&gt;(); public void Begin() { Current = new SessionManager(); } public ISessionManager Current { get { return _current.Value; } set { _current.Value = value; } } public void End(bool doComplete = true) { bool isActive = Current.Transaction != null &amp;&amp; Current.Transaction.IsActive; if (doComplete &amp;&amp; isActive) Current.Commit(); else if (!doComplete &amp;&amp; isActive) Current.Transaction.Rollback(); Current.Dispose(); } } </code></pre> <p>The SessionManager is as such:</p> <pre><code>public class SessionManager : ISessionManager { /// &lt;summary&gt; /// Initializes a new instance of the &lt;see cref="SessionManager"/&gt; class. /// &lt;/summary&gt; public SessionManager() { Session = ContextFactory.OpenSession(); Transaction = Session.BeginTransaction(); } public ITransaction Transaction { get; private set; } public ISession Session { get; private set; } public void Commit() { try { Transaction.Commit(); } catch (Exception ex) { Transaction.Rollback(); throw; } } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload