4

I'm flapping in the wind, so I thought I'd ask here... Please let me know if this is obvious and has been answered before.

I'm building an MVC 3 site which works fine while I'm running it with one user, where I click through the pages. However, if I madly hit refresh, eventually I hit a "Session is closed".

I've isolated out almost all of all my repository to try to get to the bottom, so I know have it erroring on the homepage. The only thing that is being called in the repository is get the user's name from a database table.

I'm using Postgres as a database, and the ASP.NET Membership provider from NauckIT. The main database is also Postgres (but another database).

The session management is done using the following code:

public class MvcApplication : System.Web.HttpApplication
{
    public static ISessionFactory SessionFactory = 
             NHibernateHelper.GetSessionFactory();

    public MvcApplication()
    {
        this.BeginRequest += MvcApplication_BeginRequest;
        this.EndRequest += MvcApplication_EndRequest;
    }

    void MvcApplication_BeginRequest(object sender, EventArgs e)
    {
        CurrentSessionContext.Bind(SessionFactory.OpenSession());
    }
    void MvcApplication_EndRequest(object sender, EventArgs e)
    {            
        CurrentSessionContext.Unbind(SessionFactory).Dispose();
    }
}

The code that get's the login information is:

    public Login GetCurrentLogin()
    {
        return Session.Query<Login>().FirstOrDefault(l => l.UserID == UserAccessRepository.UserID);
    }

The UserAccessRepository simply gets the userid from the forms authentication cookie.

Session is injected into the repository using:

        ninjectKernel.Bind<IUserRepository>().To<NHUserRepository>();
        ninjectKernel.Bind<ILeagueRepository>().To<NHLeagueRepository>().InThreadScope();
        ninjectKernel.Bind<ISession>()
            .ToMethod(m => MvcApplication.SessionFactory.GetCurrentSession())

The sessionfactory comes from:

public class NHibernateHelper
{
    private static ISessionFactory _sessionFactory;

    public static ISessionFactory SessionFactory
    {
        get
        {
            if (_sessionFactory == null)
            {       var rawConfig = new Configuration();
                rawConfig.SetNamingStrategy(new PostgresNamingStrategy());
                var configuration = Fluently.Configure(rawConfig)
                    .Database(PostgreSQLConfiguration.Standard.ConnectionString(ConnectionString).ShowSql().Dialect("NHibernate.Dialect.PostgreSQL82Dialect"))
                    .Mappings(m =>
                                m.AutoMappings.Add(AutoMap.AssemblyOf<Event>(new AutoMapConfiguration())
                )).ExposeConfiguration(cfg => 
                    cfg.SetProperty("current_session_context_class", "web")
                _sessionFactory = configuration.BuildSessionFactory();
                Debug.WriteLine("Built SessionFactory");
            }
            return _sessionFactory;

To be clear, it works fine in the standard instance where I click through the pages, but when I madly hit F5, I get the session closed issue.

UPDATE: Not sure if it's relevant, but the main place I'm seeing this in the BaseController, from within the OnActionExecuting method. It seems to have cleared up in the methods above.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Martin
  • 2,180
  • 4
  • 21
  • 41
  • My first question would be: why the whole `Begin/EndRequest` setup instead of injecting per-session lifetime scoped `ISession` into the controller? – Patryk Ćwiek May 20 '13 at 19:56
  • first example I found of session per request... happy for an alternative.. got a link? – Martin May 20 '13 at 19:57
  • @Trustme-I'maDoctor that's a pretty common way to do it... not saying it's the best way, but I definitely see it all over the place. – BlakeH May 20 '13 at 19:58
  • @SonicTheLichen Weird, it's the first time I've seen it. It *feels* more straightforward to inject the dependency with correct lifetime scope... But whatever :) Anyway, [here's a simple link I've googled with Ninject + ASP.NET MVC](http://www.shahnawazk.com/2010/12/dependency-injection-in-aspnet-mvc-3.html); I don't know Ninject, but I'm sure there's an option to set the dependency lifetime scope to 'per HTTP request'. – Patryk Ćwiek May 20 '13 at 20:01
  • Ninject has InRequestScope() – BlakeH May 20 '13 at 20:03
  • tried everything, changed to using the "Basic approach" from Ayende http://ayende.com/blog/4101/do-you-need-a-framework, still get the error, any ideas? – Martin May 20 '13 at 22:21
  • Doesn't `CurrentSessionContext.Unbind(SessionFactory).Dispose();` dispose the session factory and not the session? – ta.speot.is May 20 '13 at 22:40
  • @ta.speot.is The Unbind method returns the ISession object from the current context, that's then disposed – Martin May 21 '13 at 07:43

1 Answers1

1

You should not be using InThreadScope() in a web app. Use InRequestScope(). EDIT Have a read of Object Scopes - its been updated recently and not knowing it backwards is going to waste your time sooner or later!

If you're looking to make stuff work across a membership provider and request processing, you need to search for Ninject Custom Provider (maybe something like here).

Community
  • 1
  • 1
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • The membership provider is separate, and uses direct ADO.NET not NHibernate, therefore the Session is not used for this portion. I'll try replacing that and report back – Martin May 21 '13 at 10:39
  • @Martin Good - that's one less source of confusion for you so. Bottom line is you need to a) bind stuff that needs `Dispose`al in a scope other than [unspecified (which is implicitly Transient) or] Transient but b) doing stuff `InThreadScope()` both prevents the Disposal at the end of the request and causes a a random Disposal at some point in the future (and usage of such object across multiple requests that just happen to have been dispatched [for some of their processing] on the same thread – Ruben Bartelink May 21 '13 at 12:31
  • :D that worked, had to change everything to InRequestScope. I may be able to reduce that over time with some optimisation (i.e work out those that actually need to be InRequestScope and those that don't), but for now it works. – Martin May 21 '13 at 16:38