Ayende @ Rahien

Unnatural acts on source code

Do you need a framework?

I had a discussion about a session life style management, and the guy I was talking with mentioned a framework that would handle that for me. It made me think for a while, because my first instinct was to ask, what for?

Here is how I usually handle session life style management in web applications nowadays:

public class Global: System.Web.HttpApplication
{
	public static ISessionFactory SessionFactory = CreateSessionFactory();
	
	protected static ISessionFactory CreateSessionFactory()
	{
		return new Configuration()
			.Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "hibernate.cfg.xml"))
			.BuildSessionFactory();
	}
	
	public static ISession CurrentSession
	{
		get{ return (ISession)HttpContext.Current.Items["current.session"]; }
		set { HttpContext.Current.Items["current.session"] = value; }
	}
	
	protected void Global()
	{
		BeginRequest += delegate
		{
			CurrentSession = SessionFactory.OpenSession();
		};
		EndRequest += delegate
		{
			if(CurrentSession != null)
				CurrentSession.Dispose();
		};
	}
}

And yes, I copy / paste it or recreate it from scratch whenever I need to handle this in a new application. To be frank, it is simpler to do so than to try to package a it in a resuable form that would make sense to use.

Now, if you wanted support for things like multiple session life styles, then you are starting to talk about enough complexity to justify using a framework. But for the most part, code such as the one above is all that you would require to get things done, because it most applications, you only require a single lifestyle.

The same is true for the other cases as well, not just session life style management. There is a reason that I don’t particularly like commons and util libraries. In order to make such things useful as libraries, you have to satisfy a wide variety of scenarios, which complicate your life. I find that single purpose, single use, “snippets” (for lack of a better word) work better for me for the simple infrastructure concerns.

Comments

Jeremy Likness
08/05/2009 07:42 PM by
Jeremy Likness

Just make sure people clue into the "single use" aspect.

I've seen people say, "Wow, that's just two lines of code, why not just paste it wherever you need it."

"Because then if it changes, that's a lot of search and replace."

If it's used once, I agree, why not just cut and paste. But if it is simple, has a single purpose, but is used often, that is where putting it in a "common" spot or as a "util" makes sense so if the behavior does change, it can change in one place.

People have differing views of what goes in a "util" library. I think in many cases the instant you reference a business class or a domain entity, you are not longer in the realm of util.

Scott White
08/05/2009 08:07 PM by
Scott White

I use something similar using SessionScopes

DaRage
08/05/2009 08:48 PM by
DaRage

I never got the pattern of a session per request demonstrated above. To me the session is a logical unit of work and a single request can have many of those.

For example in one request i'm creating a new user, putting order for it and sending the shipping information and I would use a session for each of them.

Nicolas Penin
08/05/2009 09:15 PM by
Nicolas Penin

Hi,

You said that it is simpler to copy paste than packaging it in a reusable form. I think that encapsulating it in an HttpModule would take you less than 5 min to have it packaged and would prevent you from copy/paste it or doing it from scratch in every new project. Don't you think so ?

Dmitry
08/05/2009 09:18 PM by
Dmitry

I agree with Nicolas. HttpModule's basically are reusable Global.asax.cs files and they do not interfere with application specific logic that might be in the Global.asax.cs.

Igor T.
08/05/2009 09:20 PM by
Igor T.

Where/how are you calling the Flush()/Commit()/etc methods?

Ayende Rahien
08/05/2009 09:54 PM by
Ayende Rahien

Nicolas,

Yes, it would.

Now I would need to manage configuration for it, I would need to carry on the DLL that contains it.

It is more of a mess than helping, just from dependency management perspective.

Ayende Rahien
08/05/2009 09:54 PM by
Ayende Rahien

DaRage,

You tend not to do things like that, just because the user doesn't do things like that.

But note that a session doesn't equate to a transaction.

Ayende Rahien
08/05/2009 09:55 PM by
Ayende Rahien

Igor,

Transactions are handled in a separate manner. Usually as part of the controller level, which orchestrate operations

Phil
08/05/2009 10:09 PM by
Phil

I typically let my IOC container take care of creating sessions for me. I use StructureMap to cache the session factory as a singleton and then I have a session injected into the controller.

Bunter
08/05/2009 10:47 PM by
Bunter

That's heresy! :)

But you can have simple libs, you know.

Ayende Rahien
08/05/2009 10:49 PM by
Ayende Rahien

Bunter,

No, I can't. Not and manage them.

The cost of managing the dependencies is just too high

Damien Guard
08/05/2009 11:11 PM by
Damien Guard

Is SessionFactory.OpenSession() very lightweight because you call it on every request even if you have pages that don't need it.

I would have thought the lazy construction in CurrentSession get would have been a little safer.

[)amien

Ayende Rahien
08/05/2009 11:17 PM by
Ayende Rahien

Damien,

OpenSession is very light, yes. It just create a new session, and the session create a few objects. It is all in memory, and no DB is involved at all.

Nick Aceves
08/05/2009 11:52 PM by
Nick Aceves

Oren,

How do you usually refer to your session in services, controllers, etc? Do you inject the current session using Windsor's factory method capability?

I'm guessing that you're NOT actually referencing "Global.CurrentSession" everywhere...

Ayende Rahien
08/05/2009 11:54 PM by
Ayende Rahien

Nick,

The container injects that.

Setup is something like:

Container.Register(Component.For <isession()

.FactoryMethod(() => Global.CurrentSession)

.LifeStyle.Is(LifestyleType.Transient));

Dmitry
08/06/2009 12:53 AM by
Dmitry

Oren, any reason why you are not using SessionFactory.GetCurrentSession() with currentsessioncontext_class="web" instead of manually setting the HttpContext.Current.Item[""]?

Ayende Rahien
08/06/2009 12:58 AM by
Ayende Rahien

Dmitry,

Because it would take me more time to write it up that way than this way, and because I have been doing it this way since before GetCurrentSession().

Den
08/06/2009 01:47 AM by
Den

Oren, what about session management in WindowsForms or WPF application? For example, the main form with grid and the dialog window to edit the current object with collections.

If it is a one session conversation, then how to load previous objects states, if the user was modify the data and press Cancel button?

If it is a two session process then how to update main form persistent object, if the user was modify the data and pess Ok button?

P.S. Thank you for your open source mission.

Mike
08/06/2009 05:23 AM by
Mike

@Ayende

I +1 this post and its underlying premise. I abandoned a few 'common' libraries on my current project just because there was too much friction given the speed of development.

I also bring in the unit tests for these snippets, if present since they take on a life of their own. You agree with doing this?

Fabio Maulo
08/06/2009 05:36 AM by
Fabio Maulo

And not only that Oren.

For who what work with session-per-request + test and GetCurrentSessionContext the framework is NHibernate itself...

But believe me that it is really hard to explain.

Tobin Harris
08/06/2009 08:09 AM by
Tobin Harris

I also +1 this approach too. I actually got a bit further and have a code snippet on GitHub:

http://gist.github.com/108433

I swipe this and modify on a per-project basis. I'd rather do that than worry about crafting and maintaining one complex DLL that works for all projects.

Ayende Rahien
08/06/2009 08:13 AM by
Ayende Rahien

Den,

I have a full article about it, which will be publish soon

Rafal
08/06/2009 09:24 AM by
Rafal

@Fabio - I've been asking just about this feature (contextual session through GetCurrentSession() ) on NHusers group few days ago but couldn't make it work in my setup. So I'm basically doing it manually, like Ayende.

Nathan
08/06/2009 09:35 AM by
Nathan

Why doesn't NHibernate ship this in an assembly? I know when getting started with NHibernate just figuring out how to glue it into your application is little tricky.

If its so common you can copy and paste with impunity into every project, I dont see why NHibernate doesn't just give it to us as part of its distribution.

Ayende Rahien
08/06/2009 10:01 AM by
Ayende Rahien

Nathan,

Because this is a single use scenario, it is not generally applicable.

And NH does have builtin support for that.

Dmitry
08/06/2009 02:10 PM by
Dmitry

@Rafal,

You were probably missing CurrentSessionContext.Bind(Session); CurrentSessionContext.Unbind(SessionFactory); statements. You need the first statement after the session is opened and the second statement after the session is closed.

I like to use contextual session because I have a class managing NHibernate sessions in the data access assembly. This approach makes testing easier.

However, the new .NET System.Web.Abstractions assembly does let you mock HttpContext; but you would have to use HttpContextBase instead of HttpContext.

Mikael Henriksson
08/06/2009 07:12 PM by
Mikael Henriksson

Great post great way of tackling unnecessary complexity that doesn't even need to be there!

zvolkov
08/06/2009 07:31 PM by
zvolkov

As always Ayende's specific recipe is wrong (don't copy/paste) but the thinking is useful.

Bunter
08/08/2009 10:50 PM by
Bunter

You will still have to reference NHibernate. And depending on the database, DB libs as well. Having one additional lib with simple framework code for is is just adding ... another reference.

Ayende Rahien
08/08/2009 11:51 PM by
Ayende Rahien

Bunter,

Take a note that there is a big difference between a few cohesive frameworks and a lot of minor, unrelated, details.

Jorgas
09/02/2009 08:57 AM by
Jorgas

zvolkov,

Even though your solution also was interesting it did include a nessecity to use both CastleWindsor and MVCContrib, so I think your way of doing things are a bit more involved and complicated (arguably).

However, one point was raised in the comments that at least I think is important. How does one achieve having this behavior, but only actually opening a session when needed? Not in every request? Even though the opening of a session is VERY lightweight, it would be nice to avoid it.

Any ideas anyone?

Comments have been closed on this topic.