PDA

View Full Version : OpenSessionInView in Spring's NHibernate Integration project


steinard
01-26-2007, 09:22 AM
Hi!

Working with Spring.Net and NHibernate has been a major time-saviour and several projects have become dependent on these frameworks. Currently I've developed my own session management module to enable simple reuse of nhibernate sessions on the middleware server (using component services [com+]), where a session's life lasts roughly for a usecase (which may need several views and server communication points to be fulfilled). This logic works, but I'm not very satisfied with the solution so I thought I should give the HibernateTransactionManager and OpenSessionInViewModule a try instead.

So, before stopping all progress in other parts of the project, how is the maturity of this implementation?

Is there any documentation anywhere explaining how to configure and use the OpenSessionInView module? I had hoped to find something nice here (http://www.springframework.net/doc-latest/reference/html/orm.html)!

Thanks for any input,
Steinar.

Mark Pollack
01-26-2007, 05:01 PM
Hi Steinar,
HibernateTemplate and the transaction manager for 1.0.2 have been used by several groups successfully for > 6-8 months now. The newer 1.2 support has not had as much battle testing but it is very much the identical code base. There are some open issues regardng using non-hsql based queries and one just came up with critiera api usage that i need to look into. However, I believe you can always fallback to the lowest common demoninator "execute" callback that exposes the session to overcome these issues in a pinch.

The OpenSessionInViewModule, while in CVS for a long time, was not performing correct caching of the Session. Erich fixed this issue a few weeks ago and that is working now. I'm sorry about the lack of docs. You need to register the module and the config of OpenSessionInView is pretty "raw" at the moment using well known keys in the standard application settings section of the config file. The key names are prefixed with the fully qualified type name of OpenSessionInViewModule and then have ".SessionFactoryObjectName", ".EntityInterceptorOjectName", ".SingleSession" added to them. The minimal config doesn't specify anything in the standard app config section and relies on the name of your session factory in the spring context to be named "sessionFactory". Hope this helps.
Cheers,
Mark

Erich Eichinger
01-27-2007, 01:44 AM
Hi Steinar,

As you are talking about OpenSessionInViewModule, I assume you are talking about web applications and would like to add a note to avoid any misunderstanding:

OSIV does *not* support spanning a nhibernate session across several requests. Its intended use is having a single session during 1 request and to ensure, that exactly this single session will be closed at the end of each request.

Maybe your existing solution provides some additional benefits? Please let us know - maybe we can integrate it.

cheers,
Erich

steinard
01-29-2007, 05:33 PM
Hi!

Is it possible to use OSIV without an HTTP request/response for binding the NHibernate session (I'm not currently developing a web-app, but the service should be able to be exposed that way)? I don't mind closing the session when ending the unit of work? (I guess it might prove to avoid some other difficulties like stale data...).

In my current implementation (hack?) I use pageId and a generated sessionId to control the NHibernate session. The pageId is mapped to a use-case and can span more than one client request-response reusing the NHibernate session while working on this pageId (for most cases it will close the session at response).

If you would like to take a peek at the source then ping me back!

Thanks,
Steinar.

Erich Eichinger
01-29-2007, 07:20 PM
Hi Steinar,

I refactored the OpenSessionInViewModule a little bit and exposed it's properties and methods. Now you can create a standalone instance which can also be useful for testing.

cheers,
Erich

Mark Pollack
01-29-2007, 08:17 PM
Hi,

I gather that something along the lines of what is in this post would be good as well.

http://forum.springframework.net/showthread.php?t=676

A SessionScope that can wrap mutiple transactions...

Mark

Erich Eichinger
01-29-2007, 08:28 PM
Hi,

basically the mentioned SessionScope class is just a guard for calling the newly exposed OpenSessionInViewModule's methods Open() and Close().

I was thinking of adding it, but decided to wait for more demand - especially as it is quite easy to write such a guard e.g. as:

class SessionScopeGuard : IDisposable
{
private OpenSessionInViewModule osiv;

public SessionScopeGuard(OpenSessionInViewModule osiv)
{
this.osiv = osiv;
this.osiv.Open();
}

public void Dispose()
{
this.osiv.Close();
}
}


If you think it should be there right now, let me know!

cheers,
Erich

steinard
02-02-2007, 11:49 AM
Hi!

I'm currently looking into how I can use OSIV without having an instance of HttpApplication providing process start/end context. Would it be very inconvenient to wrap the HttpApplication into another class and make OSIV work against that class instead, this could also remove the coupling to system.web as suggested here (http://opensource.atlassian.com/projects/spring/browse/SPRNET-363).


public interface IServiceApplication
{
event EventHandler BeginRequest;
event EventHandler EndRequest;
}

public class SpringServiceApplication : HttpApplication, IServiceApplication {}



Define an IServiceModule that contain the Init method similar to the IHttpModule:

public interface IServiceModule
{
void Init(IServiceApplication context);
}



Then make the OpenSessionInViewModule bind independently on implementation to the provided context:

/// <summary>
/// Enabling open session in view for any service that can be started and ended.
/// </summary>
/// <param name="context"></param>
public void Init(IServiceApplication context)
{
String fullName = GetType().FullName;
NameValueCollection appSettings = (NameValueCollection)ConfigurationUtils.GetSection ("appSettings");

if (appSettings != null)
{
String s1 = appSettings[fullName + ".SessionFactoryObjectName"];
String s3 = appSettings[fullName + ".EntityInterceptorObjectName"];
String s2 = appSettings[fullName + ".SingleSession"];

if (StringUtils.HasLength(s1))
{
SessionFactoryObjectName = s1;
}

if (StringUtils.HasLength(s3))
{
EntityInterceptorObjectName = s3;
}

if (StringUtils.HasLength(s2))
{
SingleSession = bool.Parse(s2);
}
}

context.BeginRequest += new EventHandler(Context_BeginRequest);
context.EndRequest += new EventHandler(Context_EndRequest);
}



Currently I've made a subclass of OpenSessionInViewModule implementing the IServiceModule which defines the Init method as seen above.

Please give your considerations on the usability of such an approach.

Thanks,
Steinar.

Erich Eichinger
02-02-2007, 11:56 AM
Hi,

as long as you don't call Init() you don't need an IHttpApplication instance.

Just create an instance of OSIV, attach a SessionFactory and call Open()/Close() as needed by your code:


OpenSessionInViewModule osiv = new OpenSessionInViewModule();
osiv.SessionFactory = <your session factory>;

try
{
osiv.Open();

// do something important
}
finally
{
osiv.Close();
}


-Erich

steinard
02-02-2007, 12:52 PM
That is great, the possibility to not call Init never struck me as I don't know the method call conventions for this class (I simply thought that the Init method had to be called)!

I guess the Init will be called from somewhere when making a webapp, I just wonder when this call is made (triggered from a config file or by a class)? Is it my responsibility to call this method, or will it be called somewhere from the inside of the framework?

Thanks,
Steinar.

Erich Eichinger
02-02-2007, 01:09 PM
Hi,

Init() is called by ASP.NET's HttpRuntime if you add OSIV to your web.config's <httpModules> section:


<system.web>
<httpModules>
<add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule, Spring.Data.NHibernate"/>
</httpModules>
</system.web>


This type of usage has been the original intention of this class.

I admit standalone usage has its own rights, so please consider this class as "work in progress". I'd like to refactor its logical code out into another class that can be used by either OSIV-HttpModule or some kind of SessionScope class as mentioned above.

cheers,
Erich

Flozano
02-06-2007, 03:39 PM
Hi,

Init() is called by ASP.NET's HttpRuntime if you add OSIV to your web.config's <httpModules> section:


<system.web>
<httpModules>
<add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule, Spring.Data.NHibernate"/>
</httpModules>
</system.web>


This type of usage has been the original intention of this class.

I admit standalone usage has its own rights, so please consider this class as "work in progress". I'd like to refactor its logical code out into another class that can be used by either OSIV-HttpModule or some kind of SessionScope class as mentioned above.

cheers,
Erich

Or better - a ConversationScope :)

steinard
02-18-2007, 11:09 PM
Hi!

I just wonder if anyone has tried to create a SessionScopeAttribute and a corresponding advice based on Erich's suggested usage of OSIV, as this might look right for AOP?


OpenSessionInViewModule osiv = new OpenSessionInViewModule();
osiv.SessionFactory = <your session factory>;

try
{
osiv.Open();

// do something important
}
finally
{
osiv.Close();
}


I tried to do this today but got stuck with how the session factory should be provided to the attribute as only constant expressions can be provided.


[SessionScope(SessionFactory = appConfig.SessionFactory)]
public SessionDTO Login(string userName)
{
...
}


Thanks,
Steinar.

steinard
04-01-2007, 12:09 AM
Hi!

Just a little update for those of you who have read this thread. After some refactoring of my core service architecture I found a way to wrap the ISession into an aspect and let AOP control the life of OSIV.


public class SessionScopeAdvice : IMethodInterceptor
{
private static readonly ILog log = LogManager.GetLogger(typeof(SessionScopeAdvice));

/// <summary>
/// Around advice being invoked here, continues with it's own logic until
/// invocation.proceed() is called, then the original intercepted method
/// will be called and returns a result, before this method continues
/// execution and return the (manipulated?) results of the intercepted
/// method.
/// </summary>
/// <param name="invocation"></param>
/// <returns></returns>
public object Invoke(IMethodInvocation invocation)
{
SessionScopeGuard guard = null;
try
{
guard = new SessionScopeGuard();
object returnValue = invocation.Proceed();

return returnValue;
}
finally
{
if(guard != null) guard.Dispose();
}
}
}




[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
[Serializable]
public class SessionScopeAttribute : Attribute
{
/// <summary>
/// Default empty constructor provided for reflection purposes.
/// </summary>
public SessionScopeAttribute() {}


}






/// <summary>
/// The SessionScopeGuard will guard the session and close it when the guard is disposed.
/// </summary>
public class SessionScopeGuard : IDisposable
{
private OpenSessionInViewModule osiv;

/// <summary>
/// A convenience constructor initializing the session scope guard with
/// an instanciated open session in view.
/// </summary>
public SessionScopeGuard()
{
osiv = new OpenSessionInViewModule();
osiv.SessionFactory = (ISessionFactory)
ApplicationConfig.GetDIObject(Constants.HIBERNATE_ SESSION_FACTORY);

osiv.Open();
}

/// <summary>
/// Dispose the open session.
/// </summary>
public void Dispose()
{
osiv.Close();
}

}



I haven't yet tried this on an already proxied class (proxied through spring DI conf) as I use facades around my service classes, so be aware that there might be a problem to apply yet another advice on an already proxied class.

I hope this is of interest for anyone,
Cheers,
Steinar.