NHibernate TransactionService implemenation example - nhibernate

I keep getting the following message while retrieving my domain objects:
failed to lazily initialize a collection of role no session or session was closed
I know the problem has something to do with lazy loading of the collection on my domain object and i'm trying to fix this, but it would be nice if someone could point me in the right direction. The problem is that i have a using statement on my session object and i would like to get rid of the sessions in my repository classes.
Stefan Steinegger recommended to use a TransactionService wich manages the transactions in the following post:
C# Shared Transactions and NHibernate using IRepository
It would be nice someone can provide a tutorial, example on how to implement such a service.

There's a few different ways that you can handle this within a web application and probably the most common in web applications is a Session Per Web Request.
Inside of Application_Start in global.asax.cs, create the SessionFactory and assign it to a static property:
public static ISessionFactory SessionFactory { get; private set; }
protected void Application_Start(object sender, EventArgs e)
{
// your configuration setup
var configuration = new NHibernate.Cfg.Configuration().Configure();
SessionFactory = configuration.BuildSessionFactory();
}
Then, in Application_BeginRequest in global.asax.cs, open a session using the SessionFactory and bind it to the CurrentSessionContext
protected void Application_BeginRequest(object sender, EventArgs e)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
}
and in Application_EndRequest in global.asax.cs, unbind the session and dispose of it
protected void Application_EndRequest(object sender, EventArgs e)
{
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Dispose();
}
Now inside of the application, whenever a session is required, we simply ask the SessionFactory for the current session
public class HomeController : Controller
{
public ActionResult Index()
{
IEnumerable<Product> products = null;
var session = MvcApplication.SessionFactory.GetCurrentSession();
using (var transaction = session.BeginTransaction())
{
// your query, which sounds like it should also eager load
// a child collection
products = session.QueryOver<Product>().List();
transaction.Commit();
}
return View(products);
}
}
There are numerous variations on this, including using a LazySessionContext to lazily create a session only if one is needed for a request, and implementations where ISessionFactory could be injected into you controllers through dependency injection.

Related

How to retrieve all AutoFac components of a certain type?

I'm writing an MVC 4 application that requires the use of multiple NHibernate Session Factories. I'm registering these objects with AutoFac by name. In my global.asax, I need to retrieve all Session Factories and bind/unbind them to the CurrentSessionContext.
protected void Application_BeginRequest(object sender, EventArgs e)
{
foreach (ISessionFactory sessionFactory in SessionFactories)
{
if (!CurrentSessionContext.HasBind(sessionFactory))
{
CurrentSessionContext.Bind(sessionFactory.OpenSession());
}
}
}
protected void Application_EndRequest(object sender, EventArgs e)
{
foreach (ISessionFactory sessionFactory in SessionFactories)
{
ISession session = CurrentSessionContext.Unbind(sessionFactory);
session.Close();
CurrentSessionContext.Unbind(sessionFactory);
}
}
I'm running into a problem retrieving my SessionFactories from AutoFac. Namely, when I try to retrieve them using the Resolve method I get an empty collection. However, things work if I instead use theResolveNamed method:
SessionFactories =
new List<ISessionFactory>
{
container.ResolveNamed<ISessionFactory>("DB1"),
container.ResolveNamed<ISessionFactory>("DB2")
};
While this works, it seems rather fragile (I must update this code if any Name changes, or I add/remove SessionFactories). Is there a way to retrieve all objects from AutoFac by type that includes named instances?
Use Resolve<IEnumerable<ISessionFactory>>()
See Relationship Types for further info.

NHibernate Repository class - 1. Handle with Transaction | 2. How init session

Hi I start learn NHibernate and now I am trying create own repository class for NHibernate.
I found many articles about this thema. I chose this tutorial: http://dotnetslackers.com/Community/blogs/antrad/archive/2008/10/25/about-repository-pattern-and-nhibernate.aspx
I am little confuse how handle with transaction in repository.
I have repository interface:
public interface IRepository<T> where T : class
{
void Commit();
void RollBack();
void BeginTransaction();
//...
}
My question is why is good begin transaction in constructor class of repository.
This part of code is from blog on top.
public class NHibRepository<T> :IRepository<T>
where T : class
{
#region Private fields
private ISession _session;
#endregion
#region Constructor
public NHibRepository(ISession session)
{
_session = NHibernateSessionManager.Instance.GetSession();
//why begin transaction where???
_session.BeginTransaction();
}
#endregion
#region Trans
public void Commit()
{
if (session.Transaction.IsActive)
{
session.Transaction.Commit();
}
}
public void Rollback()
{
if (session.Transaction.IsActive)
{
session.Transaction.Rollback();
session.Clear();
}
}
public void BeginTransaction()
{
Rollback();
session.BeginTransaction();
}
#endregion
}
I update this repository and now I use this solution:
public class NHibRepository<T> :IRepository<T>
where T : class
{
#region Private fields
private ISession _session;
#endregion
#region Constructor
public NHibRepository(ISession session)
{
_session = session;
}
#endregion
#region Trans
public void Commit()
{
using (var trans=_session.BeginTransaction())
{
trans.Commit();
}
}
public void RollBack()
{
using (var trans = _session.BeginTransaction())
{
trans.Rollback();
}
}
public void BeginTransaction()
{
throw new NotImplementedException();
}
#endregion
}
My first question is which solution is better and why? or what is correct why?
And my second question is how open session for repository class, now I use this helper class:
public class NHibeHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
InitializeSessionFactory();
return _sessionFactory;
}
}
private static void InitializeSessionFactory()
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(
#"Server=Zues\SQLEXPRESS;Database=TEST;Trusted_Connection=True;")
)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>().Conventions.Add(DefaultCascade.All()))
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))
.BuildSessionFactory();
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
On use like this:
_userRepo = new NHibRepository<User>(NHibeHelper.OpenSession());
Looking at the two options you've presented for transaction management, they both actually have issues.
In example 1, the transaction isn't necessarily disposed, and also you're linking the lifetime of the transaction to the lifetime of the repository. This isn't necessarily a bad thing but unless you're using an IoC container or some other method to ensure that the repository is recreated for each database operation, you will only be able to commit once, and subsequent commits will probably fail.
Example 2 will unfortunately not work at all - in Commit/RollBack you are starting a new transaction every time. Especially in the case of RollBack, if this was called from external code while in the context of a transaction, all you will do is start a new nested transaction and then immediately roll it back. Not very useful unfortunately.
My personal solution to your first question is to pull transaction management away from the repository by using the ambient transactions in System.Transactions. This way you still have separation as ambient transactions are provider-agnostic, but also you have more control about what of many repository operations you might be performing will be involved in the transaction, rather than always using the single-transaction-per-operation model. See http://msdn.microsoft.com/en-us/magazine/cc163527.aspx for more info.
As for your second question, I would highly recommend looking into an IoC container like Castle Windsor or Unity. The helper class you have there is far better than rebuilding the session factory and such every time, but by using an IoC container you can very easily ensure the lifetime of your session object is exactly what you want it to be. For instance, in a number of WCF Per-Call services I'm working on, I use Castle Windsor to ensure that the session is created (and injected into the repository) when the WCF operation begins, and is disposed (along with the repository) when it ends.

Configuration which one should I be using? From fluent tutorial or from cookbook 3.0?

I am reading nhibernate cookbook 3.0 and the fluent tutorial and I am kinda confused which one I should be using(cookbook by itself has many different ways)
Fluent Nhibernate tutorial
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
SQLiteConfiguration.Standard
.UsingFile("firstProject.db")
)
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<Program>())
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private static void BuildSchema(Configuration config)
{
// delete the existing db on each run
if (File.Exists(DbFile))
File.Delete(DbFile);
// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config)
.Create(false, true);
}
cookbook 3.0 pg(76) web request
1. In the hibernate-configuration section of web.config, add the current_
session_context_class property with a value of web.
2. If it doesn't exist already, add a new Global application class (Global.asax).
3. In Global.asax, add these using statements.
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Context;
4. Create a static property named SessionFactory.
public static ISessionFactory SessionFactory { get;
private set; }
5. In the Application_Start method, add the following code.
protected void Application_Start(object sender, EventArgs e)
{
log4net.Config.XmlConfigurator.Configure();
var nhConfig = new Configuration().Configure();
SessionFactory = nhConfig.BuildSessionFactory();
}
6. In the Application_BeginRequest method, add the following code.
protected void Application_BeginRequest(object sender, EventArgs e)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
}
7. In the Application_EndRequest method, add the following code:
protected void Application_EndRequest(object sender, EventArgs e)
{
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Dispose();
}
Then they just use this to run it.
Guid productId = new Guid(Request["id"]);
Eg.Core.Product product;
var session = Global.SessionFactory.GetCurrentSession();
using (var tran = session.BeginTransaction())
{
product = session.Get<Eg.Core.Product>(productId);
tran.Commit();
}
Page.Title = product.Name;
Label1.Text = product.Name;
Label2.Text = product.Description;
With the fluent tutorial I am also kinda confused where I would typically put that code in an asp.net mvc application. I am trying to use the repository pattern with ninject(DI injection).
So with both ways I am not sure how to make it work with ninject and the repository pattern.
Is either way better for the repository pattern and Di?
Did you try to download the whole solution to run the project? These are just samples of code and you need the whole setup: VS project with Entity classes, mappings, repositories etc.
I would go to http://www.sharparchitecture.net/ and https://github.com/sharparchitecture download their Northwind sample project that has the exact setup that you need. You will have to find the Northwind database and install it on you local machine then modify the NHibernate.config to point to your database.

How can do I solve this? I want to inject a nhibernate session but the session is not created yet

Edit
I would like to try to do what Remo Gloor has recommended
Create a SessionFactoryProvider derived for Provider that retruns a SessionFactory using the code in OnApplicationStarted
Create a binding for SessionFactory using the new provider and put it in SingletonScope
Create a SessionProvider derived from Provider that gets the SessionFactory injected in the constructor and retuns a new session using GetCurrentSession of the factory.
Create a binding for ISession to the above provider with activation and deactivation actions that open, transmit, rollback, and close the session (basiclly the code from Application_BeginRequest, EndRequest). Decalre the binding as in request scope.
Remove Application_BeginRequest, EndRequest.
Bind the repo using Bind().To();
I am looking for a tutorial that hits on these points with a file that I can download and play around with. If it uses lots of generics you needs to be pretty detailed as generics still get me.
Hi
I am trying to do session per request with my nhibernate.
I done this in my global.aspx
using System;
using System.Web.Mvc;
using System.Web.Routing;
using Demo.WebUI.Models.NinjectModules;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Context;
using Ninject;
using Ninject.Modules;
using Ninject.Web.Mvc;
namespace Demo.WebUI
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {controller = "Account", action = "Login", id = UrlParameter.Optional} // Parameter defaults
);
}
public static ISessionFactory SessionFactory { get; private set; }
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
SessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("test")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Demo.Framework.Data.NhibernateMapping.UserMap>())
.ExposeConfiguration(x => x.SetProperty("current_session_context_class", "web"))
.ExposeConfiguration(BuidSchema)
.BuildSessionFactory();
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
ISession session = SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
protected void Application_EndRequest(object sender, EventArgs e)
{
ISession session = CurrentSessionContext.Unbind(SessionFactory);
if (session != null)
{
try
{
session.Transaction.Commit();
}
catch (Exception)
{
session.Transaction.Rollback();
}
finally
{
session.Close();
session.Dispose();
}
}
}
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new ServiceModule(),
new RepoModule()
};
return new StandardKernel(modules);
}
private static void BuidSchema(NHibernate.Cfg.Configuration config)
{
new NHibernate.Tool.hbm2ddl.SchemaExport(config).Create(false, true);
}
}
}
In my RepoModule I have
Bind<IUserRepo>().To<UserRepo>().WithConstructorArgument("session",MvcApplication.SessionFactory.GetCurrentSession());
This will throw a error because ninject will create the kernal before OnApplicationStarted() gets started and before Application_Begin starts to bind it.
So what should I do?
Edit
This is what I found in some tutorial.
public static ISessionFactory SessionFactory { get; private set; }
public MvcApplication()
{
SessionFactory = CreateSessionFactory();
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("test")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Demo.Framework.Data.NhibernateMapping.UserMap>())
.ExposeConfiguration(x => x.SetProperty("current_session_context_class", "web"))
.ExposeConfiguration(BuidSchema)
.BuildSessionFactory();
}
My binding
Bind<IUserRepo>().To<UserRepo>();
Bind<ISession>().ToMethod(x => MvcApplication.SessionFactory.GetCurrentSession());
So I first create the session factory on constructor load then I bind it to the Isession instead of passing it in as a parameter.
The only thing that I am not sure with the global aspx is if it will keep calling the constructor up everytime and recreating the SessionFactory what is bad. So I am not sure if I need to check if it exists first.
Your implementation is fine but not a good solution as you do manual control of the session lifecycle. The actual way this should be done is to let Ninject decide when the session is created, opened, closed and disposed.
E.g. imagine actions where you don't need any session. Or a larger project where you have several databases and sessions to split the load. In these situations you don't want all the possible sessions be created for each action as this means an avoidable overhead. You rather want that only those are created that are required for the current action.
To do so several changes are required:
Create a SessionFactoryProvider derived for Provider that retruns a SessionFactory using the code in OnApplicationStarted
Create a binding for SessionFactory using the new provider and put it in SingletonScope
Create a SessionProvider derived from Provider that gets the SessionFactory injected in the constructor and retuns a new session using GetCurrentSession of the factory.
Create a binding for ISession to the above provider with activation and deactivation actions that open, transmit, rollback, and close the session (basiclly the code from Application_BeginRequest, EndRequest). Decalre the binding as in request scope.
Remove Application_BeginRequest, EndRequest.
Bind the repo using Bind<IUserRepo>().To<UserRepo>();
I recently blogged about using nhibernate in an asp.net mvc application with a repository pattern. This project provides an example of using nhibernate & ninject. Here are a few links:
http://blog.bobcravens.com/2010/07/using-nhibernate-in-asp-net-mvc/
http://blog.bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/
http://blog.bobcravens.com/2010/09/the-repository-pattern-part-2/
http://blog.bobcravens.com/2010/11/using-ninject-to-manage-critical-resources/
I think that we have a similar architecture. Take a look at the posts and let me know if you have any questions.
BTW, you can download this project at http://gpsnerd.codeplex.com
Bob
Wrap the SessionFactory initialization code in a singleton, that will initialize and configure the sessionfactory once when you access the "Instance" property on it. Use this in BeginRequest instead of current code.
You're using a current session context, you don't have to inject the session!
Inject the SessionFactory instead, then use GetCurrentSession() to obtain an ISession.
After that change, you can use Felice's solution.

nhibernate : Repository Session Management

At the moment my repository has 2 constructors. When i call these from my mvc website i am alway calling first constructor and thus opening a new session. Should i been passing in the session. How should i be doing this.
public CompanyRepository()
{
_session = NHibernateHelper.OpenSession();
}
public CompanyRepository(ISession session)
{
_session = session;
}
public class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(typeof(UserProfile).Assembly);
configuration.SetProperty(NHibernate.Cfg.Environment.ConnectionStringName,
System.Environment.MachineName);
_sessionFactory = configuration.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
I'm using the Ninject IOC container ( very new to me ). I have the following container. How would i bind the ISession to the CompanyRepository.
private class EStoreDependencies : NinjectModule
{
public override void Load()
{
Bind<ICompanyRepository>().To<CompanyRepository>();
Bind<IUserProfileRepository>().To<UserProfileRepository>();
Bind<IAddressRepository>().To<AddressRepository>();
Bind<IRolesService>().To<AspNetRoleProviderWrapper>();
Bind<IUserService>().To<AspNetMembershipProviderWrapper>();
Bind<ICurrentUserSerivce>().To<DefaultCurrentUserSerivce>();
Bind<IPasswordService>().To<AspNetMembershipProviderWrapper>();
Bind<IStatusResponseRepository>().To<StatusResponseRepository>();
Bind<ICategoryRepository>().To<CategoryRepository>();
Bind<IProductRepository>().To<ProductRepository>();
}
}
You should be using the "one session per request" pattern, by storing the ISession object in the HttpContext and sharing it between repositories and queries made during the same HTTP request.
Here's an implementation using MVC action attributes.
An easy/basic implementation could also be achieved by simply altering your NHibernateHelper class like this:
public class NHibernateHelper {
//...
const string SessionKey = "NhibernateSessionPerRequest";
public static ISession OpenSession(){
var context = HttpContext.Current;
if(context != null && context.Items.ContainsKey(SessionKey)){
//Return already open ISession
return (ISession)context.Items[SessionKey];
}
else{
//Create new ISession and store in HttpContext
var newSession = SessionFactory.OpenSession();
if(context != null)
context.Items[SessionKey] = newSession;
return newSession;
}
}
}
Code hasn't been neither compiled nor tested... should work however.
Your code or, preferably, dependency injection should always pass the ISession into a repository's constructor. This allows multiple repositories to participate in a single transaction.
I second Paco's recommendation to let a dependency injection framework handle this for you. The challenge with this approach is with non-web applications that do not have clean unit-of-work boundaries like the HTTP request-response cycle. We have repositories that are shared by Windows Forms and ASP.NET applications and we manually manage newing up repositories in the Windows Forms applications.
Use an inversion of control container
Try using sessionFactory.GetCurrentSession() which will allow you to access a contextual session.
This will basically allow you to use the 'session per request' model as described in another answer, without having to code that yourself.
You can even choose what your context is: Http (as your example suggests) or a bunch of others too (I use CallSessionContext for my unit test).