I'd like to keep my transactions as short as possible. Does SM begin/commit transactions at the start/end of each web request? If so, how can I prevent this? How can I have an exception thrown when data is attempted to be accessed without creating a transaction manually?

Also, how do we implement transaction reprocessing? If a transaction fails due to a deadlock exception, I'd like for it to be reprocessed.

asked Feb 10 '11 at 23:21

ara's gravatar image

ara
395868791

edited Feb 11 '11 at 03:24


One Answer:

Does SM begin/commit transactions at the start/end of each web request

Not exactly:

  • The transaction is starting when you're accessing current session @ particular web request for the first time.
  • The transaction is completed when web request completes.

If so, how can I prevent this?

To prevent creation of transaction at all, you simply shouldn't access current session - i.e. don't use Session.Current / Demand().

Committing such a transaction earlier is currently impossible without heavy reflection. If you need, we can add this feature.

How can I have an exception thrown when data is attempted to be accessed without creating a transaction manually?

Actually, that's exactly what happens when you deal with newly created Session. Default SessionOptions is ServerProfile - i.e. it throws an exception on any attempt to access the data outside of transaction scope.

But SessionManager is designed to automatically create the transaction along with the session, and you don't have a chance to prevent this. If this could be prevented, you'd get exactly what you need.

So in your case you need a bit different behavior: SessionManager must provide just session, but not a session + transaction.

There are two solutions:

  1. You can fix this by writing your own session manager based on our code. SessionManager is part of practices project, so its source code is fully available.
  2. We can provide an option there allowing to control this.

Possible approach:

// Global option, must be set on app. start
SessionManager.OpenTransaction = false | true; 
  // default = true

// Or a local one, must be set before accessing 
// Session.Current for the first time in
// the current web request.
SessionManager.Current.OpenTransaction = false | true; 
  // default = null = inherit.

Also, how do we implement transaction reprocessing?

Shortly we'll offer fully automatic integrated solution. For now I can only recommend to ignore this issue.

answered Feb 11 '11 at 09:56

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

edited Feb 11 '11 at 09:56

To avoid these automatic transactions, in my controller actions I am explicitly opening a new session using SessionManager.Domain.OpenSession, then opening a transaction with that session, doing my work, then completing the transaction scope. The session and transaction are opened in "using" scopes.

1) Is this best practice?

2) Would accessing SessionManager.Domain perform any of the side effects that I am trying to avoid? I don't see any reason why it should, but just want to make sure.

3) Is there any other functionality I miss out on by using this approach that Session Manager provides?

(Mar 14 '11 at 06:44) ara ara's gravatar image

1) Yes. Probably a good idea is to extract the code managing\creating such Session for you to a controller method (e.g. your own descendant of Controller or extension method). In this case you'll be able to simplify handling e.g. deadlocks further

2) It has only one side effect: it builds the domain, if it isn't built yet. The code of getter of this property:

EnsureDomainIsBuilt();
return domain;

3) No, there is nothing you're missing.

(Mar 16 '11 at 02:37) Alex Yakunin Alex%20Yakunin's gravatar image

Addition to 1): We use this base class for our controllers (there are few other intermediates, but this one is direct descendant of Controller):

public class SessionBoundController : Controller
{
    private Xtensive.Orm.Session session;

    public HttpSessionStateBase SessionState {
        get { return base.Session; }
    }

    public new Xtensive.Orm.Session Session {
        get {
            if (session==null)
                session = Xtensive.Orm.Session.Demand();
            return session;
        }
    }
}
(Mar 16 '11 at 02:39) Alex Yakunin Alex%20Yakunin's gravatar image

So you can use a similar approach by extending it with these methods:

    public TransactionScope TransactionScope
    {
        get { return transactionScope; }
    }

    public IDisposable OpenOwnSession()
    {
        var oldSession = session;
        session = null;
        session = Domain.OpenSession();
        return new Disposable(b => {
            session.DisposeSafely();
            session = oldSession;
        });
    }
(Mar 16 '11 at 02:45) Alex Yakunin Alex%20Yakunin's gravatar image

Continuing:

    public IDisposable OpenOwnTransaction(bool commit = true)
    {
        var disposable = OpenOwnSession();
        transactionScope = Session.OpenTransaction();
        return new Disposable(b => {
            if (commit)
                transactionScope.Complete();
            transactionScope.DisposeSafely();
            transactionScope = null;
            disposable.DisposeSafely();
        });
    }
(Mar 16 '11 at 02:45) Alex Yakunin Alex%20Yakunin's gravatar image

Hopefully this will help.

(Mar 16 '11 at 02:45) Alex Yakunin Alex%20Yakunin'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

Subscription:

Once you sign in you will be able to subscribe for any updates here

Tags:

×9

Asked: Feb 10 '11 at 23:21

Seen: 3,156 times

Last updated: Mar 16 '11 at 02:45

powered by OSQA