Is there any problem if I did this :

using(Session.Open(domain, true))
  using(var trx = Transaction.Open())
  {
    myObject.DoSomething();
  }

if DoSomething() method also opens a session and transaction the same way ? I mean will everything run in the same session and transaction context or not?

asked Sep 02 '10 at 04:34

msameer's gravatar image

msameer
29559

edited Sep 02 '10 at 05:07

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412


One Answer:

Imagine MyObject.DoSomething has the following code and study the comments:

public void DoSomething()
{
  // Session.Demand() == outerSession
  var someEntity1 = new SomeEntity(); // someEntity.Session == outerSession
  using (var session = Session.Open(domain, true)) {
    // Session.Demand() == session
    // session.Transaction == null
    using (var tx = Transaction.Open()) {
      // session.Transaction != null here
      var someEntity2 = new SomeEntity(); // someEntity.Session == session
      tx.Complete()
    }
    // session.Transaction == null
  }
  // Session.Demand() == outerSession
}

Hopefully, this explains everything. Note that "current" term is local to each thread.

One more important aspect related to session activation is "Session switching":

  using (var sessionA = Session.Open(domain)) { // Open & activate
    var personA = Query.All<Person>().First();
    using (var sessionB = Session.Open(domain)) { // Open & activate
      using(var tx = Transaction.Open()) {
        AssertEx.Throws<InvalidOperationException>(() => {
          var personAName = personA.Name; // Will fail because of Session switching attempt
        });
      }
    }
  }

The following happens here:

  • personA belongs to SessionA, i.e. personA.Session==sessionA
  • transaction is running inside previously
  • but SessionB is active inside innermost using block
  • when you read personA.Name, our [Transactional] aspect, that is automatically applied to all public members of all ISessionBound implementors, tries to activate its own session (i.e. sessionA.
  • but is does this using `Session.Activate(bool checkSwitching) method, that throws an exception, if another session is already active, and transaction is running there. That's so-called session switching check".

Note that if there is no transaction in sessionB, no exception will be thrown:

  using (var sessionA = Session.Open(domain)) { // Open & activate
    var personA = Query.All<Person>().First();
    using (var sessionB = Session.Open(domain)) { // Open & activate
      var personAName = personA.Name; // Ok, since there is no running transaction in sessionB
    }
  }

So by default, automatic activation of one session inside another one with open transaction (i.e. when another session is active, and transaction is already running there) leads to exception. This is intentional: normally this indicates the same thread controls two sessions and transactions simultaneously, and thus there is a chance of getting application-level deadlock.

Application-level deadlock happens e.g. when the same thread sequentially:

  • Accesses some resource in sessionA (so SQL Server acquires shared lock on it in that session)
  • Tries to modify the resource in sessionB, so SQL Server tries to acquire X-lock on it for another session, but to do this, it must wait for release of the first lock. Thus it starts to wait for this, but this will never happen, because the thread controlling both sessions is also waiting for completion of current operation.

Note that such deadlocks can't be automatically detected by SQL Server, since it doesn't knows two connections are actually controlled by the same thread. Thus finally it will cancel running operation by timeout - i.e. two transactions will be running for ~ 30 seconds by default, and this will negatively affect on other operations.

Alternatively, session switching might indicate unintentional usage of data fetched by one session inside another - that's bad again, since normally all the data you access in a single transaction must be acquired there (to ensure it's protected unexpected modifications by other transactions - i.e. isolated).

But what if session switching is intentional?

There are actually two cases, when this can be intentional:

  1. When you deal with UI sesions with attached DisconnectedStates. It's normally ok here, since you don't hit the DB.
  2. The case you 100% know this is intentional :)

And, correspondingly, there are two ways of blocking session switching check:

  1. Using SessionOptions.AllowSwitching option. If both Sessions participating in check have this flag, session switching check doesn't happen.
  2. Using Session.Deactivate() static method. Any session can be activated inside using block with this method.

Example of way 1:

  var cfg = new SessionConfiguration();
  cfg.Options |= SessionOptions.AllowSwitching;

  using (var sessionA = Session.Open(domain, cfg)) { // Open & activate
    var personA = Query.All<Person>().First();
    using (var sessionB = Session.Open(domain, cfg)) { // Open & activate
      using(var tx = Transaction.Open()) {
        var personAName = personA.Name; // Will not fail
      }
    }
  }

Example of way 2:

  using (var sessionA = Session.Open(domain, cfg)) { // Open & activate
    var personA = Query.All<Person>().First();
    using (var sessionB = Session.Open(domain, cfg)) { // Open & activate
      using(var tx = Transaction.Open()) {
        using (Session.Deactivate()) {
          var personAName = personA.Name; // Will not fail
        }
      }
    }
  }

answered Sep 02 '10 at 09:18

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

edited Sep 02 '10 at 09:35

1

This should be writed in manual, this session switching scenario if it is not already there, because it is good to know how sessions works with DO4.

(Sep 02 '10 at 14:14) Peter Ĺ ulek Peter%20%C5%A0ulek's gravatar image
1

I'll leave a link to this topic there ;) Our Manual already tends to be overcomplicated...

(Sep 02 '10 at 16:07) Alex Yakunin Alex%20Yakunin's gravatar image

Thank you very much Alex. The behavior is clearer.

(Sep 07 '10 at 07:18) msameer msameer's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA