如何使用注入存储库的Isessionfactory来管理ninject和Nhibernate的事务?

Joy*_*Joy 1 nhibernate aop ninject repository-pattern

我是DI和IoC模式的新手.

public class LazySessionContext
{
    private readonly ISessionFactoryImplementor factory;
    private const string CurrentSessionContextKey = "NHibernateCurrentSession";

    public LazySessionContext(ISessionFactoryImplementor factory)
    {
        this.factory = factory;
    }

    /// <summary>
    /// Retrieve the current session for the session factory.
    /// </summary>
    /// <returns></returns>
    public ISession CurrentSession()
    {
        Lazy<ISession> initializer;
        var currentSessionFactoryMap = GetCurrentFactoryMap();
        if (currentSessionFactoryMap == null ||
            !currentSessionFactoryMap.TryGetValue(factory, out initializer))
        {
            return null;
        }
        return initializer.Value;
    }

    /// <summary>
    /// Bind a new sessionInitializer to the context of the sessionFactory.
    /// </summary>
    /// <param name="sessionInitializer"></param>
    /// <param name="sessionFactory"></param>
    public static void Bind(Lazy<ISession> sessionInitializer, ISessionFactory sessionFactory)
    {
        var map = GetCurrentFactoryMap();
        map[sessionFactory] = sessionInitializer;
    }

    /// <summary>
    /// Unbind the current session of the session factory.
    /// </summary>
    /// <param name="sessionFactory"></param>
    /// <returns></returns>
    public static ISession UnBind(ISessionFactory sessionFactory)
    {
        var map = GetCurrentFactoryMap();
        var sessionInitializer = map[sessionFactory];
        map[sessionFactory] = null;
        if (sessionInitializer == null || !sessionInitializer.IsValueCreated) return null;
        return sessionInitializer.Value;
    }

    /// <summary>
    /// Provides the CurrentMap of SessionFactories.
    /// If there is no map create/store and return a new one.
    /// </summary>
    /// <returns></returns>
    private static IDictionary<ISessionFactory, Lazy<ISession>> GetCurrentFactoryMap()
    {
        var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<ISession>>)
                                HttpContext.Current.Items[CurrentSessionContextKey];
        if (currentFactoryMap == null)
        {
            currentFactoryMap = new Dictionary<ISessionFactory, Lazy<ISession>>();
            HttpContext.Current.Items[CurrentSessionContextKey] = currentFactoryMap;
        }
        return currentFactoryMap;
    }
}

public interface ISessionFactoryProvider
{
    IEnumerable<ISessionFactory> GetSessionFactories();
}

public class SessionFactoryProvider
{
    public const string Key = "NHibernateSessionFactoryProvider";
}

public class NHibernateSessionModule : IHttpModule
{
    private HttpApplication app;

    public void Init(HttpApplication context)
    {
        app = context;
        context.BeginRequest += ContextBeginRequest;
        context.EndRequest += ContextEndRequest;
        context.Error += ContextError;
    }

    private void ContextBeginRequest(object sender, EventArgs e)
    {
        var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key];
        foreach (var sf in sfp.GetSessionFactories())
        {
            var localFactory = sf;
            LazySessionContext.Bind(
                new Lazy<ISession>(() => BeginSession(localFactory)),
                sf);
        }
    }

    private static ISession BeginSession(ISessionFactory sf)
    {
        var session = sf.OpenSession();
        session.BeginTransaction();
        return session;
    }

    private void ContextEndRequest(object sender, EventArgs e)
    {
        var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key];
        var sessionsToEnd = sfp.GetSessionFactories()
                               .Select(LazySessionContext.UnBind)
                               .Where(session => session != null);

        foreach (var session in sessionsToEnd)
        {
            EndSession(session);
        }
    }

    private void ContextError(object sender, EventArgs e)
    {
        var sfp = (ISessionFactoryProvider)app.Context.Application[SessionFactoryProvider.Key];
        var sessionstoAbort = sfp.GetSessionFactories()
                                .Select(LazySessionContext.UnBind)
                                .Where(session => session != null);

        foreach (var session in sessionstoAbort)
        {
            EndSession(session, true);
        }
    }

    private static void EndSession(ISession session, bool abort = false)
    {
        if (session.Transaction != null && session.Transaction.IsActive)
        {
            if (abort)
            {
                session.Transaction.Rollback();
            }
            else
            {
                session.Transaction.Commit();
            }
        }
        session.Dispose();
    }

    public void Dispose()
    {
        app.BeginRequest -= ContextBeginRequest;
        app.EndRequest -= ContextEndRequest;
        app.Error -= ContextError;
    }
}
Run Code Online (Sandbox Code Playgroud)

我从GitHub的chinooknugets和jfmarillo那里得到了这个样本.从上面的代码我将会话注入存储库并通过IHttpmodule控制事务.现在我有两个问题:

  1. 如果我通过上面的代码实现事务管理,只要有请求就会调用它,它会为该请求打开一个会话.这是实现"按请求进行会话"方法的唯一目的.但是,如果我的所有控制器方法中只有一个实际使用存储库的方法,那么每次我发出请求时都不想打开会话.仅在控制器中由Transaction属性标记的操作期间才会处理该事务.

  2. 我会发出请求,只有在存储库请求时才会打开会话,因此可以通过任何IoC容器实现.

  3. 我仍然希望httpcontext事件处理事务,以便context + = BegingRequest和context + = EndRequest.并且该事务将在httpsodule内部处理,并根据请求提供httpcontext.但我不想实现IhttpModule并放入web.config.这种方法还有其他选择吗?

  4. 会话打开和关闭只能在那些httpcontext中完成,但是我想通过IoC容器(最好是ninject)来管理它,但只有当存储库正在请求该会话时.请注意,可以在调用控制器时初始化存储库,但不应该在该存储库中打开会话.当实际存储库执行任何临时操作时,会话应该打开.

有人会澄清我应该遵循这种情况的做法吗?我正在使用带有ninject和nhibernate的Mvc 3.

Rem*_*oor 5

1,2)Ninject允许使用Ninject.Extensions.Factory 3.0.0创建Lazy对象

class MyController
{
    public MyController(Lazy<SomeRepository> repository) { ... }
}
Run Code Online (Sandbox Code Playgroud)

这样就可以在使用时创建存储库(和上下文).

3)为什么要为此使用HttpModule?有更简单的方法,例如:

kernel.Bind<ISession>()
      .ToMethod(ctx => ctx.Kernel.Get<ISessionFactory().OpenSession())
      .InRequestScope()
      .OnActivation(session => OpenTransaction(session))
      .OnDeactivation(session => EndTransaction(session));
Run Code Online (Sandbox Code Playgroud)

从Ninject 3.0.0开始,您可以为HttpModules添加绑定,而不是在web.config中注册它们,并为它们使用construcotr注入.但由于HttpModule不知道是否使用了上下文,因此您必须为所有请求打开事务.