简体   繁体   English

应用程序设计:NH会话管理,通用存储库,ASP.NET MVC

[英]Application design: NH-session management, generic repository, ASP.NET MVC

Having defined a domain model I want to figure out how to do the rest of work. 定义了domain model我想弄清楚如何完成其​​余的工作。


DATA ACCESS LAYER 数据访问层

I had read before that it is not necessary to code own UnitOfWork implementation over ISession (thogh I found a much information on how to do it pretty well). 我之前已经读过,没有必要在ISession上编写自己的UnitOfWork实现代码(因此,我发现了很多有关如何很好地实现它的信息)。 So I'm quite confused.. I have repository interface like this: 所以我很困惑。我有这样的存储库接口:

public interface IRepository<T> where T: AbstractEntity<T>, IAggregateRoot
{
    T Get(Guid id);
    IQueryable<T> Get(Expression<Func<T, Boolean>> predicate);
    IQueryable<T> Get();
    T Load(Guid id);
    void Add(T entity);
    void Remove(T entity);
    void Remove(Guid id);
    void Update(T entity);
    void Update(Guid id);
}

Where in the concrete implementation there are two options: 在具体实现中有两个选择:

OPTION A 选项A

Is to inject ISessionFactory thru constructor and have something similar to: 是通过构造函数注入ISessionFactory并具有类似于以下内容的内容:

public class Repository<T> : IRepository<T> where T : AbstractEntity<T>, IAggregateRoot
{
    private ISessionFactory sessionFactory;

    public Repository(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }
    public T Get(Guid id)
    {
        using(var session = sessionFactory.OpenSession())
        {
            return session.Get<T>(id);
        }
    }
}

OPTION B 选项B

Is to use NHibernateHelper class 是使用NHibernateHelper

using(var session = NHibernateHelper.GetCurrentSession())
{
    return session.Get<T>(id);
}

Where NHibernateHelper is NHibernateHelper在哪里

internal sealed class NHibernateHelper
{
    private const string CurrentSessionKey = "nhibernate.current_session";
    private static readonly ISessionFactory sessionFactory;

    static NHibernateHelper()
    {
        sessionFactory = new Configuration().Configure().BuildSessionFactory();
    }

    public static ISession GetCurrentSession()
    {
        HttpContext context = HttpContext.Current;
        ISession currentSession = context.Items[CurrentSessionKey] as ISession;

        if(currentSession == null)
        {
            currentSession = sessionFactory.OpenSession();
            context.Items[CurrentSessionKey] = currentSession;
        }

        return currentSession;
    }

    public static void CloseSession()
    {
        HttpContext context = HttpContext.Current;
        ISession currentSession = context.Items[CurrentSessionKey] as ISession;

        if(currentSession == null)
        {                
            return;
        }

        currentSession.Close();
        context.Items.Remove(CurrentSessionKey);
    }

    public static void CloseSessionFactory()
    {
        if(sessionFactory != null)
        {
            sessionFactory.Close();
        }
    }
} 

What's option is prefered? 首选什么选项?

Why(besides the injection)? 为什么(除了注射)?

If I use option A where do I place configuration of ISessionFactory ? 如果使用选项A ,应在哪里放置ISessionFactory配置?

Should it be placed somewhere in ASP.NET MVC project? 是否应将其放置在ASP.NET MVC项目中的某个位置? How? 怎么样?

Thank you for reading the monster-question! 感谢您阅读怪物问题! Your guidance is appreciated! 感谢您的指导!

How to handle injecting dependencies with mvc is somewhat version specific but it always helps to use a real Dependency Injection (DI) container. 如何使用mvc处理注入依赖项在某种程度上是特定于版本的,但是使用真正的依赖注入(DI)容器总是有帮助的。 However you slice it, this solution will need you to Inject an ISession into the Repository rather than an ISessionFactory. 无论您如何切片,此解决方案都需要您将ISession而不是ISessionFactory注入到存储库中。 This allows your DI container to manage the lifetime of the session properly. 这使您的DI容器可以正确管理会话的生存期。

Assuming you're using Asp.Net MVC 3 and dont have an attachment to a specific DI container already, fire up your Nuget console and type: 假设您使用的是Asp.Net MVC 3,并且还没有特定DI容器的附件,请启动您的Nuget控制台并键入:

install-package Ninject.MVC3

This will go, download Ninject (which is a DI container) and configure your mvc application to use it. 这样就可以下载Ninject(这是一个DI容器)并配置您的mvc应用程序以使用它。 It will also create a file ~/App_Start/NinjectMVC3.cs which is where you'll configure your dependencies as such. 还将创建一个〜/ App_Start / NinjectMVC3.cs文件,您可以在其中配置依赖项。

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<ISessionFactory>()
        .ToMethod(c => new Configuration().Configure().BuildSessionFactory())
        .InSingletonScope();

    kernel.Bind<ISession>()
        .ToMethod((ctx) => ctx.Kernel.Get<ISessionFactory>().OpenSession())
        .InRequestScope();

    kernel.Bind<IRepository<>>().To<Repository<>>();        
}   

The first statement tells ninject that when something requires an ISessionFactory, it should lazily initialize NHibernate and create one. 第一条语句告诉ninject,当某些东西需要ISessionFactory时,它应该延迟NHibernate初始化并创建一个。 This session factory is then to be held as an application-wide singleton for the lifetime of the application. 然后,该会话工厂将在应用程序的整个生命周期内作为整个应用程序范围的单例保存。

The second statement tells ninject that when something requires an ISession, it should get an instance of ISessionFactory and call OpenSession(). 第二条语句告诉ninject,当某些东西需要ISession时,它应该获取ISessionFactory的实例并调用OpenSession()。 This Session is then reused within the scope of the request and destroyed at the end of the request. 然后,该会话在请求范围内被重用,并在请求结束时销毁。

The third statement tells ninject that when something requires an IRepository of any type, it should just new one up using it's built in logic to resolve dependencies. 第三条语句告诉ninject,当某事物需要任何类型的IRepository时,它应该只是使用它的内置逻辑来解决依赖关系的新事物。

From here you can write your code as follows and everything should just work. 在这里,您可以按照以下方式编写代码,一切都应该可以正常工作。

public class WidgetController : Controller
{
    private readonly IRepository<Widget> _repository;
    public WidgetController(IRepository<Widget> repository)
    {
        _repository = repository;
    }
}

With regards to the Repository I'd like to point you to an excelent blog post Repository is the new Singleton 关于存储库,我想向您介绍一个出色的博客文章, 存储库是新的Singleton

I usually use a read only property, on my repository, like this 我通常在存储库中使用一个只读属性,如下所示

    protected  ISession Session
    {
        get
        {
            return NHibernateSessionFactory.CurrentFor(dataBaseFactoryKey);
        }
    }

My NHibernateSessionFactory works like this . 我的NHibernateSessionFactory像这样工作

In web apps you should use pattern NH session per web request. 在网络应用中,每个网络请求都应使用模式NH会话。 I think you should have only one session per web request and your repositories should use this single session. 我认为您每个Web请求应该只有一个会话,并且您的存储库应该使用此单个会话。 To implement this you need to write IHttpModule which will open session, begin transaction and bind session as ambient (current) session when request begins and end transaction and close session when request ends. 要实现此目的,您需要编写IHttpModule,它将在请求开始时打开会话,开始事务并将绑定会话绑定为环境(当前)会话,并在请求结束时结束事务并关闭会话。 You also need to set current_session_context_class to "web". 您还需要将current_session_context_class设置为“ web”。 Then your Repository/DAO will look like this 然后您的存储库/ DAO将如下所示

    public TEntity Get(object id)
    {
        return sessionFactory.GetCurrentSession().Get<TEntity>(id);
    }

Best pattern with MVC and NHibernate is session per request. 使用MVC和NHibernate的最佳模式是每个请求会话。
steps: 脚步:

  1. In Global.asax add public static ISessionFactory SessionFactory ; 在Global.asax中添加公共静态ISessionFactory SessionFactory
  2. In Application_Start() configure and build session factory: Application_Start()中配置和构建会话工厂:

    var config = new Configuration().Configure(); var config = new Configuration()。Configure();
    SessionFactory = config.BuildSessionFactory(); SessionFactory = config.BuildSessionFactory();

  3. In Application_BeginRequest open the session and bind it to CurrentSessionContext: Application_BeginRequest中,打开会话并将其绑定到CurrentSessionContext:

    var nhSession = SessionFactory.OpenSession(); var nhSession = SessionFactory.OpenSession();
    CurrentSessionContext.Bind(Session); CurrentSessionContext.Bind(Session);

  4. In Application_EndRequest() unbind and dispose the session Application_EndRequest()中解除绑定并处置会话

Now in your controller you can access your session invoking: 现在,在您的控制器中,您可以访问会话调用:

Global.SessionFactory.GetCurrentSession();

EDIT: Following @hival comment 编辑: @hival评论后

Inside your controller handle your model in a using block and perform commit/rollback based on your logic. 控制器内部将在using块中处理模型,并根据逻辑执行提交/回滚。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM