How do I setup my repository with fluent nhibernate?
I am trying to use the repository pattern, ninject for DI with fluent nhibernate.
I broken up my solution into 3 projects
web ui - controllers,view (I am using asp.net mvc 2.0)
framework - repository, domain, fluent mapping
tests - where my unit tests will go.
So I have setup this up.
Global.aspx
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}
So I put in my application start to use ninject controller factory.
NinjectControllerFactory
public class NinjectControllerFactory : DefaultControllerFactory
{
// A Ninject "kernel" is the thing that can supply object instances
private IKernel kernel = new StandardKernel(new T4GDemoSevice());
// ASP.NET MVC calls this to get the controller for each request
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType == null)
return null;
return (IController)kernel.Get(controllerType);
}
// Configures how abstract service types are mapped to concrete implementations
private class DemoSevice : NinjectModule
{
public override void Load()
{
Bind<ISessionFactory>().ToMethod(c => GetSessionFactory()).InSingletonScope();
Bind<ISession>().ToMethod(c => c.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
Bind<IUsers>().To<UsersRepo>().WithConstructorArgument("session",GetSessionFactory());
}
public static ISessionFactory GetSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("test")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<T4G_Demo.Framework.Data.NhibernateMapping.UserMap>())
.BuildSessionFactory();
}
}
}
Here I setup my ninject stuff. I made a method that sets up fluent. As I understand it this should only happen once per web request(which I think it does since this only gets call in the application start)
I am not sure though if my GetSessionFactory method should be static.
I then bind my IUsers interface to my UserRepo and and pass in a constructor argument. I am guessing that my Repos need the session of nhibernate to do something. So I was not sure how to pass it to my repos.
I think what I have might be wrong as each repository would get it's on configuration??
Repository
public class UsersRepo : IUsers
{
private ISessionFactory session;
public UsersRepo(ISessionFactory session)
{
this.session = session;
}
public void CreateUser(Domain.Users newUser)
{
var openSession = session.OpenSession();
var transaction = openSession.BeginTransaction();
openSession.SaveOrUpdate(newUser);
transaction.Commit();
}
}
I tried to make a repository but again I am not sure how to do it with the session. Like I have to open it up and begin the transaction. I am not sure if that should be done somewhere else.
I was also not sure if I should dispose it after it goes through the methods. I seen people have dispose methods in the application_end().
My Home Controller
private IUsers usersRepository;
public HomeController(IUsers usersRepository)
{
this.usersRepository = usersRepository;
}
public ActionResult Index()
{
Users test = new Users()
{
OpenIdIdentifier = "123",
Email = "as@hotmail.com",
};
usersRepository.CreateUser(test);
return View();
}
Finally I just made a dummy controller that creates a dummy user and inserts it into the db. It works but like I said the session part confuses me as I am not sure how to pass it along.
Edit
This is what I have come up with so far. It still not what I want(I rather have a session per request) but it seems to be working.
Ninject factory
public static ISessionFactory GetSessionFactory()
{
if (session == null)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("test")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<T4G_Demo.Framework.Data.NhibernateMapping.UserMap>())
//.ExposeConfiguration(BuidSchema)
.BuildSessionFactory();
}
return session;
}
I added a if statement to check if the session is started or not. So this should take care of the problem of starting SessionFactory each time.
My repo looks like this
public class UsersRepo : IUsers
{
private ISession openSession;
private ISessionFactory session;
public UsersRepo(ISessionFactory session)
{
this.openSession = session.OpenSession();
this.session = session;
}
public void CreateUser(Users newUser)
{
openSession = NhibernateUtilities.OpenIfClosed(session, openSession);
openSession.SaveOrUpdate(newUser);
}
public Users GetUser(Guid userI开发者_运维知识库d)
{
openSession = NhibernateUtilities.OpenIfClosed(session, openSession);
var query = from b in openSession.Query<Users>()
where b.UserId == userId
select b;
return query.SingleOrDefault();
}
public void DeleteUser(Users user)
{
openSession = NhibernateUtilities.OpenIfClosed(session, openSession);
openSession.Delete(user);
}
public void SaveOrUpdate()
{
using (openSession)
{
using (var transaction = openSession.BeginTransaction())
{
transaction.Commit();
}
}
}
So in each method I check if a session is open if not then I open one through this method.
public static ISession OpenIfClosed(ISessionFactory session, ISession openSession)
{
if (openSession.IsOpen == false)
{
return session.OpenSession();
}
return openSession;
}
Like I said I would love to get rid of this method but I am unsure how to get the session then over to the repos. So until someone can show me I guess I have to do it like this for now.
first, you might consider using the Ninject.Web.Mvc extension, since it already has the NinjectControllerFactory and a NinjectHttpApplication that wires up the controller factory for you. this also allows you to create the IKernel in your application, which is where it should be, not in your controller factory.
second, the SessionFactory is very expensive to create, so it should only be done ONCE for the lifetime of the application, i.e., a Singleton.
here is the module I'm using in my applications:
public class RepositoriesModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
// NHibernate
Bind<ISessionFactory>().ToProvider<SessionFactoryBuilder>()
.InSingletonScope();
Bind<ISession>()
.ToMethod( CreateSession )
.InRequestScope();
// Model Repositories
Bind( typeof( IRepository<> ) ).To( typeof( NHibernateRepository<> ) ).InScope( HttpOrThread );
Bind<IGameRepository>().To<GameRepository>();
Bind<ILogRepository>().To<LogRepository>();
Bind<IMemberRepository>().To<MemberRepository>();
Bind<IScopeManager>().To<ScopeManager>();
}
private ISession CreateSession( Ninject.Activation.IContext context )
{
var session = context.Kernel.Get<ISessionFactory>().OpenSession();
session.EnableFilter( DigitalLights.Model.FluentLogicalDeleteFilter.FilterName );
return session;
}
}
points of interest - the ISessionFactory is tied to a Provider in Singleton scope. the Provider simple creates the SessionFactory once on startup. here's what the Provider looks like.
/// <summary>
/// factory builder that uses FluentNHibernate to configure and return a SessionFactory
/// </summary>
public class SessionFactoryBuilder : Ninject.Activation.IProvider
{
private ISessionFactory _sessionFactory;
/// <summary>
/// constructor configures a SessionFactory based on the configuration passed in
/// </summary>
/// <param name="configuration"></param>
public SessionFactoryBuilder()
{
/// setup configuration here
var fluentConfig = Fluently.Configure()
.Database( MsSqlConfiguration.MsSql2005.ConnectionString( connectionString ) );
this._sessionFactory = fluentConfig.BuildSessionFactory();
}
#region IProvider Members
public object Create( Ninject.Activation.IContext context )
{
return _sessionFactory;
}
public Type Type
{
get { return typeof( ISessionFactory ); }
}
#endregion
}
next, the NHibernate ISession is create by the method CreateSession which retrieves the ISessionFactory from the Kernel. the ISession is scoped to the request. then, the repository implementation simply asks for an ISession in it's constructor.
also, in my app, in BeginRequest a ITransaction is started, and in EndRequest the ITransaction is committed, to make sure the session is persisted and closed.
精彩评论