Ninject with NHibernate and .NET MVC

1,067 views
Skip to first unread message

Dave

unread,
Nov 17, 2009, 4:10:08 PM11/17/09
to ninject

What is the best way to manage NHibernate ISession with Ninject in
a .NET MVC application? I've been looking around net-land and have
figured out that session-per-request is the best approach but I
haven't seen an example that covers everything that needs to be done
from session creation to deletion, from the start of a request to the
end. Can someone give me some tips or give me a link?

The other problem I've been trying to solve is how to use Ninject in
an application that contains different assemblies, all of which have
their own DI requirements. All the Ninject examples I've seen are
made up of a single assembly, which appears to be simple to setup and
configure. But what if you have more than one assembly, each of which
has their own StandarModule derived class that needs to be loaded at
startup time?

Thanks,
Dave.

Jarrett Meyer

unread,
Nov 17, 2009, 4:28:42 PM11/17/09
to nin...@googlegroups.com
For clarification, you have SomeAssemblyA.dll and SomeAssemblyB.dll, and each has a NinjectModule or StandardModule inside that Assembly?

In my current project, I have multiple assemblies, but none of the base-level assemblies have any reference to Ninject. They all use plain-old-ctor injection.

In my top-level application, I have LinqToSqlDataContextModule and WebContextModule. Each of these modules builds up injection bindings for their respective assemblies.

private static IKernel BuildKernel()
{
            var modules = new INinjectModule[]
            {
                // Data context for LINQ-to-SQL database operations.
                new LinqToSqlDataContextModule(() =>
                    {
                        var builder = new LinqToSqlContextBuilder();
                        return builder.Build(new EbirsDataContext());
                    }),
                // Builds up all of my presentation-layer dependencies
                new WebContextModule(),
                // Builds up all of my business-layer dependencies (mostly event observers)
                new BusinessModule()
            };

--
Jarrett Meyer
Email: jarret...@gmail.com
Web: JarrettMeyer.com



--

You received this message because you are subscribed to the Google Groups "ninject" group.
To post to this group, send email to nin...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/ninject?hl=.



Miguel Madero

unread,
Nov 18, 2009, 6:56:13 AM11/18/09
to nin...@googlegroups.com
You sort of described one of my previous projects.

Disclaimer: I don't have a lot of experience with NHibernate, so I'm not sure I took the right approach. This was a really small/pet project and it just worked for our scenario, so don't take my advice blindly.

1. We have our MvcApplication class which inherits from NinjectHttpApplication. I overrode the CreateKernel method and call kernel.Load("CompanyName.*.dll"). That will load all the Modules from any referenced assembly that match the pattern and look for all of the types that implement INinjectModule. This will make all the modules in all of my assemblies self discoverable.
2. I have an NHibernateModule
    public class NHibernateModule : NinjectModule
    {
        public override void Load()
        {
            var configuration = new NHibernateConfiguration();
            Bind<ISessionFactory>().ToConstant(configuration.GetSessionFactory());
            Bind<ISession>().ToMethod(x => x.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
        }
    }

    I'll go into details of the NHConfiguration. The other two lines of code are just setting bindings. Constant for the Session Factory and a new Session (aquired from the SessionFactory) on a RequestScope. I tested this and it worked well on our project, but this blog post mentiones that InRequestScope doesn't work on Ninject 1.5 and this would be the difference
        // Change the previous binding for ISession for this one that relies on this method.
        Bind<ISession>().ToMethod(x => GetRequestSession(x))
        internal const string SESSION_KEY = "NHibernate.ISession";
        private ISession GetRequestSession(IContext context)
        {
            var dict = HttpContext.Current.Items;
            var session = default(ISession);
            if(!dict.Contains(SESSION_KEY))
            {
                session = context.Kernel.Get<ISessionFactory>().OpenSession();
                dict.Add(SESSION_KEY, session);
            }
            else
            {
                session = (ISession) dict[SESSION_KEY];
            }
            return session;
        }

3. NHConfiguration will vary from project to project, so only use this as an example:
    public class NHibernateConfiguration
    {
        public ISessionFactory GetSessionFactory()
        {
            return Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.FromConnectionStringWithKey("CompanyName")))
                .Mappings(x => x.AutoMappings.Add(AutoMap.AssemblyOf<DayMenu>()
                    .Where(t => t.Namespace.StartsWith("CompanyName.Entities"))
                    .Conventions.Add<RelationshipTableConvention>()))
                .Mappings(m=> m.FluentMappings.AddFromAssemblyOf<DayMenuMap>()
                    .Conventions.Add<RelationshipTableConvention>())
                .BuildSessionFactory();
        }
    }

I'm using FluentNH. I think it's great, but you could go with hbm files, but the rest stays the same.
4. On your Controller, Services or other classes. Just add a dependenecy to ISession as you would usually do. I'm using the repository pattern for our root entities, so I've the dependencies for ISession only there and the other classes depend on the specific Repositories.


Please let me know if you have any other questions.

Hope this gives you an idea.
--
Miguel A. Madero Reyes
www.miguelmadero.com (blog)
m...@miguelmadero.com

Jason Dentler

unread,
Nov 18, 2009, 7:19:20 AM11/18/09
to nin...@googlegroups.com

Miguel Madero

unread,
Nov 18, 2009, 8:42:00 AM11/18/09
to nin...@googlegroups.com
That's the same link that I referred to. I forgot to mentioned about disposing the ISession. This is important if you don't use the InRequestScope method.
In the App you will have to add the following (from the blog post):

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


private void MvcApplication_EndRequest(object sender, System.EventArgs e)
{
if (Context.Items.Contains(NHibernateModule.SESSION_KEY))
{
NHibernate.ISession Session = (NHibernate.ISession) Context.Items[NHibernateModule.SESSION_KEY];
Session.Dispose();
Context.Items[NHibernateModule.SESSION_KEY] = null;
}


}

Dave

unread,
Nov 18, 2009, 10:51:43 AM11/18/09
to ninject
Thanks Miguel,

That helps alot! Just one question: how do you handle the ISession in
unit tests? Do you use injection or do you just create the ISession
manually and then pass it around?

Dave

On Nov 18, 6:42 am, Miguel Madero <m...@miguelmadero.com> wrote:
> That's the same link that I referred to. I forgot to mentioned about
> disposing the ISession. This is important if you don't use the
> InRequestScope method.
> In the App you will have to add the following (from the blog post):
>
>  public MvcApplication()
>         {
>             this.EndRequest += MvcApplication_EndRequest;
>         }
>
>         private void MvcApplication_EndRequest(object sender,
> System.EventArgs e)
>         {
>             if (Context.Items.Contains(NHibernateModule.SESSION_KEY))
>             {
>                 NHibernate.ISession Session = (NHibernate.ISession)
> Context.Items[NHibernateModule.SESSION_KEY];
>                 Session.Dispose();
>                 Context.Items[NHibernateModule.SESSION_KEY] = null;
>             }
>
>         }
>
> On Wed, Nov 18, 2009 at 11:19 PM, Jason Dentler <jasondent...@gmail.com>wrote:
>
>
>
> > Hi Dave,
>
> > I think this is what you need:
>
> >http://jasondentler.com/blog/2009/08/part-7-nhibernate-and-ninject-fo...
>
> > Thanks,
> > Jason
>
> > On Wed, Nov 18, 2009 at 5:56 AM, Miguel Madero <m...@miguelmadero.com>wrote:
>
> >> You sort of described one of my previous projects.
>
> >> Disclaimer: I don't have a lot of experience with NHibernate, so I'm not
> >> sure I took the right approach. This was a really small/pet project and it
> >> just worked for our scenario, so don't take my advice blindly.
>
> >> 1. We have our MvcApplication class which inherits from
> >> NinjectHttpApplication. I overrode the CreateKernel method and call
> >> kernel.Load("CompanyName.*.dll"). That will load all the Modules from any
> >> referenced assembly that match the pattern and look for all of the types
> >> that implement INinjectModule. This will make all the modules in all of my
> >> assemblies self discoverable.
> >> 2. I have an NHibernateModule
> >>     public class NHibernateModule : NinjectModule
> >>     {
> >>         public override void Load()
> >>         {
> >>             var configuration = new NHibernateConfiguration();
>
> >> Bind<ISessionFactory>().ToConstant(configuration.GetSessionFactory());
> >>             Bind<ISession>().ToMethod(x =>
> >> x.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
> >>         }
> >>     }
> >>     I'll go into details of the NHConfiguration. The other two lines of
> >> code are just setting bindings. Constant for the Session Factory and a new
> >> Session (aquired from the SessionFactory) on a RequestScope. I tested this
> >> and it worked well on our project, but this blog post<http://nhforge.org/blogs/nhibernate/archive/2009/08/29/part-7-nhibern...>mentiones that InRequestScope doesn't work on Ninject 1.5 and this would be
> >> On Wed, Nov 18, 2009 at 8:28 AM, Jarrett Meyer <jarrettme...@gmail.com>wrote:
>
> >>> For clarification, you have SomeAssemblyA.dll and SomeAssemblyB.dll, and
> >>> each has a NinjectModule or StandardModule inside that Assembly?
>
> >>> In my current project, I have multiple assemblies, but none of the
> >>> base-level assemblies have any reference to Ninject. They all use
> >>> plain-old-ctor injection.
>
> >>> In my top-level application, I
> >>> have LinqToSqlDataContextModule and WebContextModule. Each of these modules
> >>> builds up injection bindings for their respective assemblies.
>
> >>> private static IKernel BuildKernel()
> >>> {
> >>>             var modules = new INinjectModule[]
> >>>             {
> >>>                 // Data context for LINQ-to-SQL database operations.
> >>>                 new LinqToSqlDataContextModule(() =>
> >>>                     {
> >>>                         var builder = new LinqToSqlContextBuilder();
> >>>                         return builder.Build(new EbirsDataContext());
> >>>                     }),
> >>>                 // Builds up all of my presentation-layer dependencies
> >>>                 new WebContextModule(),
> >>>                 // Builds up all of my business-layer dependencies
> >>> (mostly event observers)
> >>>                 new BusinessModule()
> >>>             };
>
> >>> --
> >>> Jarrett Meyer
> >>> Email: jarrettme...@gmail.com
> >>> Web: JarrettMeyer.com

Dave

unread,
Nov 18, 2009, 11:32:59 AM11/18/09
to ninject
> I overrode the CreateKernel method and call kernel.Load("CompanyName.*.dll").

Can you expand a little more on this? where is "kernel" coming from
and where is the "Load" method?

Miguel Madero

unread,
Nov 18, 2009, 7:24:21 PM11/18/09
to nin...@googlegroups.com
For most most of the Tests I don't use the ISession, I either test the model or the controller in isolation. For my Data Tests and configuration tests I'm using xUnit and the PersistenceSpecification (part of Fluent NHibernate).

This is my NHibernateSessionContext class. My Data Test classes inherit from this:

public class NHibernateSessionContext :  TrasanctionalContext
    {
        protected readonly ISession _session;

        public NHibernateSessionContext()
        {
            _session = new NHibernateConfiguration().GetSessionFactory().OpenSession();
        }
    }

TransactionalContext just starts and rollbacks the transaction
public class TrasanctionalContext : IDisposable
    {
        private readonly TransactionScope _transactionScope;

        public TrasanctionalContext()
        {
            _transactionScope = new TransactionScope();
        }

        public void Dispose()
        {
            _transactionScope.Dispose();
        }
    }


Finally a typical mapping test class would look something like this:

public class MappingsTests : NHibernateSessionContext
    {

        [Fact]
        public void CanMapMainCourse()
        {
            var mainCourses = DayMenuStubber.GetMainCourses();
            mainCourses.ForEach(mc=>
                new PersistenceSpecification<MainCourse>(_session)
                    .CheckProperty(c => c.Name, mc.Name)
                    .CheckProperty(c => c.Description, mc.Description)
                    .CheckProperty(c => c.Price, mc.Price)
                    .CheckProperty(c => c.IsVegetarian, mc.IsVegetarian)
                    .VerifyTheMappings()
            );
        }

The DayMenuStubber will just return me a list of MainCourses. Nothing fancy there, but here's the code just for the sake of completion

public static ObservableCollection<MainCourse> GetMainCourses()
        {
            return new ObservableCollection<MainCourse>
            {
                new MainCourse
                {
                    Id = 1,
                    Name = "Penne Stroganoff",
                    Description = "marinated beef strips sautéed with Mushrooms stirred in a rich Sour Cream",
                    IsVegetarian = false,
                    Price = 10.0f,
                },
                new MainCourse
                {
                    Id = 2,
                    Name = "Low-fat crispy chicken",
                    Description = "chicken breast covered with mushrooms, parmesan cheese and aromatic herbs) with Spring Rice ( colourful veggies)",
                    IsVegetarian = false,
                    Price = 10.0f,
                },
                new MainCourse
                {
                    Id = 3,
                    Name = "Eggplant Stroganoff",
                    Description = "vegetarian version of the famous stroganoff sauce",
                    IsVegetarian = true,
                    Price = 10.0f,
                }
            };
        }



Now. What's really happening?
xUnit works slightly different to other testing frameworks. So instead of relying on a TestInitialize and TestCleanup methods (usually decorated with attributes as with MSTest), it uses the constructor to intialize your "context", which means it will create one instance of your test class for each test and it will call dispose (if your class implements IDisposable). You can really easily map this concepts to MSTest or NUnit. Just intialization and cleanup is handled slightly differently.
The PersistenceSpecification will use the provided ISession to test the CRUD operations for the specified properties and associations, it will also compare the retrieved entitiy with the specified values in the lambda expressions (e.g. .CheckProperty(c => c.Name, mc.Name)). It also checks for concurrency and other stuff. Basically this test will just check that your mapping is right.


For the other stuff I use Ninject.Extensions.Moq to get the "automocking" facilities. That way if I don't specify a binding for some of the dependencies it will automatically inject some mocks. That makes things really easy since I don't have to manually create those mocks. However I didn't use this a lot, since most of my tests are for the model which don't have any dependencies that I needed to mock and in some of the controllers I went for manual mocking of the entities using manual Stubs (like with the method above).



--

You received this message because you are subscribed to the Google Groups "ninject" group.
To post to this group, send email to nin...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/ninject?hl=.


Reply all
Reply to author
Forward
0 new messages