如何使用流畅的nhibernate设置我的存储库?

时间:2011-01-14 19:10:51

标签: c# asp.net-mvc-2 fluent-nhibernate repository-pattern ninject

我正在尝试使用存储库模式,ninject用于流利的nhibernate。

我将我的解决方案分解为3个项目

web ui - controllers,view (I am using asp.net mvc 2.0)
framework - repository, domain, fluent mapping
tests - where my unit tests will go.

所以我已经设置好了。

Global.aspx

  protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterRoutes(RouteTable.Routes);

            ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
        }

所以我把我的应用程序开始使用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();
            }

        }
    }

在这里我设置了我的ninject东西。我做了一个流畅的方法。据我所知,这应该只发生一次每个Web请求(我认为它确实如此,因为这只在应用程序启动时调用)

我不确定我的GetSessionFactory方法是否应该是静态的。

然后我将IUsers接口绑定到UserRepo并传入构造函数参数。我猜我的Repos需要nhibernate的会话来做某事。所以我不知道如何将它传递给我的回购。

我认为我所拥有的可能是错误的,因为每个存储库都会在配置上得到它?

存储库

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();
        }
    }

我试图创建一个存储库,但我不知道如何使用该会话。就像我必须打开它并开始交易一样。我不确定是否应该在其他地方完成。

我也不确定在完成这些方法之后是否应该处理它。我看到人们在application_end()中有处理方法。

我的家庭控制器

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();
        }

最后,我做了一个虚拟控制器,创建一个虚拟用户并将其插入到数据库中。它的工作原理,但就像我说会话部分让我困惑,因为我不知道如何传递它。

修改

这是我到目前为止所提出的。它仍然不是我想要的(我宁愿每个请求有一个会话)但它似乎正在工作。

Ninject工厂

  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;              
        }

我添加了一个if语句来检查会话是否已启动。所以这应该解决每次启动SessionFactory的问题。

我的回购看起来像这样

 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 userId)
        {
            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();
                }
            }
        }

所以在每种方法中,如果没有,我会检查会话是否打开,然后我通过这种方法打开一个。

 public static ISession OpenIfClosed(ISessionFactory session, ISession openSession)
        {
            if (openSession.IsOpen == false)
            {
                return session.OpenSession();
            }

            return openSession;
        }

就像我说的那样,我很乐意摆脱这种方法,但我不确定如何让会话进入回购。所以,直到有人能告诉我,我想我现在必须这样做。

1 个答案:

答案 0 :(得分:3)

首先,您可以考虑使用Ninject.Web.Mvc扩展,因为它已经有NinjectControllerFactory和NinjectHttpApplication为您连接控制器工厂。这也允许您在应用程序中创建IKernel,而不是在控制器工厂中。

第二,SessionFactory的创建成本非常高,所以它应该只在应用程序的生命周期内完成ONCE,即Singleton。

这是我在我的应用程序中使用的模块:

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;
    }

}

兴趣点 - ISessionFactory与Singleton范围内的Provider相关联。 Provider simple在启动时创建一次SessionFactory。这是提供商的样子。

/// <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
}

接下来,NHibernate ISession由CreateSession方法创建,该方法从内核中检索ISessionFactory。 ISession的范围是请求。然后,存储库实现只是在它的构造函数中要求一个ISession。

另外,在我的应用程序中,在BeginRequest中启动了ITransaction,并在EndRequest中提交了ITransaction,以确保会话持久化并关闭。