NInject, NHIbernate and ISession in request scope

1.1k views Asked by At

I'm basically using an example which I've seen in many places but I'm not convinced it's working properly.

So, I have the following:

public class NHibernateHelper
{
    private readonly string _connectionString;
    private ISessionFactory _sessionFactory;

    public ISessionFactory SessionFactory
    {
        get { return _sessionFactory ?? (_sessionFactory = CreateSessionFactory()); }
    }

    public NHibernateHelper(string connectionString)
    {
        _connectionString = connectionString;
    }

    private ISessionFactory CreateSessionFactory()
    {
        return Fluently.Configure()
                .Database(FluentNHibernate.Cfg.Db.MySQLConfiguration.Standard
                    .Dialect("NHibernate.Spatial.Dialect.MySQLSpatialDialect,NHibernate.Spatial.MySQL")
                    .ConnectionString(_connectionString))
                    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<EventListing>())
                    .ExposeConfiguration(x =>
                    {
                        x.SetProperty(NHibernate.Cfg.Environment.CurrentSessionContextClass, ConfigurationManager.AppSettings["current_session_context_class"]);
                    })
                .BuildSessionFactory();
    }
}

and in my Ninject startup I have

private static void RegisterServices(IKernel kernel)
    {
        NHibernateHelper helper = new NHibernateHelper(WebConfigurationManager.ConnectionStrings["eventlisting"].ConnectionString);
        kernel.Bind<ISessionFactory>().ToConstant(helper.SessionFactory).InSingletonScope();
        kernel.Bind<ISession>().ToProvider(new SessionProvider()).InRequestScope();
        kernel.Bind(typeof(IReadOnlyRepository<>)).To(typeof(ReadOnlyRepository<>)).InRequestScope();
        kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
}

and my session provider class looks like

public class SessionProvider : Provider<ISession>
{
    protected override ISession CreateInstance(IContext context)
    {
        ISession session = DependencyResolver.Current.GetService<ISessionFactory>().OpenSession();
        session.FlushMode = FlushMode.Auto;
        return session;
    }
}

What is confusing me is that despite stating that ISession should be created in request scope, it seems to hit the CreateInstance method multiple times per request and looks to be spawning multiple sessions.

e.g. if I have a controller with this constructor:

public SomeController(
        IRepository<SomeClass> someClassRepository,
        IRepository<SomeOtherClass> someOtherClassRepository)
    {
        _someClassRepository = someClassRepository;
        _someOtherRepository = someOtherClassRepository;
    }

then the CreateInstance method gets called twice (I should add the constructor for the repositories takes an ISession parameter):

public Repository(ISession session)
    {
        this.Session = session;
    }

I thought having it as InRequestScope would mean it is only called once. Is it me misunderstanding or am I missing some code out somewhere.

1

There are 1 answers

0
BatteryBackupUnit On BEST ANSWER

I made a quick test to verify that ninject's "basic" Scoping and Provider functionality is working correctly:

public class StringProvider : Provider<string>
{
    protected override string CreateInstance(IContext context)
    {
        return context.ToString();
    }
}


var kernel = new StandardKernel();

kernel.Bind<string>().ToProvider(new StringProvider()).InSingletonScope();

kernel.Get<string>();
kernel.Get<string>();
kernel.Get<string>();

In this case, StringProvider.CreateInstance(..) is - correctly - only called once.


I believe your issue is with the correct configuration of the ninject extension which provides .InRequestScope(). The sad truth is, that when it is not configured correctly, it's not going to yell at you / throw an exception, no it's going to keep quiet. You'll only find it out once the unexpected behavior bites you in the ass ;-)

See here: ASP.NET MVC + Ninject: InRequestScope This is an instance of such an issue which i answered just yesterday.