1

我有一个使用绝对最新版本 (3.3) 的 Web 应用程序,并且在 HttpModule 中使用逐个请求会话管理,因此多个会话冲突没有问题。不幸的是,我发现会话在我执行 Transaction.Commit 后立即自动关闭,我只在实际执行创建、更新或删除时才这样做。我在我的 NHibernate 日志中找到了这个。

我知道我没有这样做,因为对 ISession.Close 函数的唯一调用是在我的 HttpModule 中完成的。

是的,当然我可以在我的 SessionManager 中放入代码来检查 IsClosed 参数,然后使用 OpenSession 函数而不是 GetCurrentSession,但这是否应该发生?有什么方法可以通过我的配置或可以在 Session 或 Transaction 对象上设置的某些属性来防止这种情况发生,或者这只是我在任何地方都找不到任何文档的新功能之一?

请帮忙。

布赖恩

我被要求提供一些代码,所以这里是 HttpModule 的代码:

public class NhibernateModule : IHttpModule
{
    public void Dispose()
    {
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
        context.EndRequest += new EventHandler(context_EndRequest);
    }

    public void context_BeginRequest(Object sender, EventArgs e)
    {
        WebSessionContext.Bind(NhibernateSessionManager.GetContextSession());
    }

    public void context_EndRequest(Object sender, EventArgs e)
    {
        ISession session = WebSessionContext.Unbind(NhibernateSessionManager.SessionFactory);

        if (session != null)
        {
            if (session.Transaction != null && session.Transaction.IsActive)
            {
                session.Transaction.Rollback();
            }
            else
                session.Flush();

            session.Close();
        }
    }
}

}

接下来,您将找到我在 SessionManager 中使用的原始代码:

public sealed class NhibernateSessionManager
{
    private readonly ISessionFactory sessionFactory;
    public static ISessionFactory SessionFactory
    {
        get { return Instance.sessionFactory; }
    }

    private ISessionFactory GetSessionFactory()
    {
        return sessionFactory;
    }

    public static NhibernateSessionManager Instance
    {
        get { return NestedSessionManager.sessionManager; }
    }

    public static ISession GetContextSession()
    {
        ISession session;
        if (CurrentSessionContext.HasBind(SessionFactory))
        {
            session = SessionFactory.GetCurrentSession();
        }
        else
        {
            session = SessionFactory.OpenSession();
            CurrentSessionContext.Bind(session);
        }
        return session;
    }  

    private NhibernateSessionManager()
    {
        if (sessionFactory == null)
        {
            Configuration configuration;
            configuration = new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "web.config"));
            log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "web.config")));

            //Configuration configuration = new Configuration().Configure();
            if (configuration == null)
            {
                throw new InvalidOperationException("NHibernate configuration is null.");
            }
            else
            {
                sessionFactory = configuration.BuildSessionFactory();
                if (sessionFactory == null)
                    throw new InvalidOperationException("Call to BuildSessionFactory() returned null.");
            }
        }
    }

    class NestedSessionManager
    {
        internal static readonly NhibernateSessionManager sessionManager = new NhibernateSessionManager();
    }
}

最后,这是一个当前导致会话在 Transaction.Commit() 之后立即关闭的函数。每个内部函数都检索当前会话,然后处理 Save 调用。

    public static Int32 AddVideo(VideoFile Video, Int32 UserID, Int16 InstID)
    {
        log.Debug("Begin AddVideo");
        Int32 FileID = 0;

        using (ISession Session = NhibernateSessionManager.GetContextSession())
        {
            using (ITransaction Transaction = Session.BeginTransaction())
            {
                Video.Created = DateTime.Now;
                Video.Modified = DateTime.Now;

                FileID = (Int32)Session.Save(Video);
                Video.FileID = FileID;

                // Need to process through all the categories and insert records into the ivxFileCategories table
                // to associate the newly created file with the chosen categories
                if (Video.CategoryAssociations != null)
                {
                    log.Info("Number of categories to be associated with the video: " + Video.CategoryAssociations.Count);
                    for (int i = 0; i < Video.CategoryAssociations.Count; i++)
                    {
                        CategoryFileAssociation Assoc = (CategoryFileAssociation)Video.CategoryAssociations[i];
                        Assoc.FileID = FileID;
                        AssociationManager.AddCategoryFileTransaction(Assoc);
                    }
                }

                // Need to add the default file access for the UserDetail that is creating the new video which will always
                // be Admin because the UserDetail creating a file should always have admin access over the file, no matter
                // what their default role is.
                AssociationManager.AddFileAccessTransaction(FileID, UserID, UserClassConstants.IVXUSERCLASS_ADMIN);

                // Need to add the institutional association based on whether the new video was created by a librarian
                // or one of the iVidix admins
                AssociationManager.AddInstitutionFileTransaction(InstID, FileID);

                Transaction.Commit();
            }
        }

        log.Debug("End AddVideo");
        return FileID;
    }
4

3 回答 3

4

会话将通过 AddVideo 方法进行处理,因为您正在使用会话的using 语句

using (ISession Session = NhibernateSessionManager.GetContextSession())
{

}
于 2012-05-01T21:25:42.060 回答
1

提交事务将结束该会话。

将您的事务开始移动到 context_BeginRequest 和 context_EndRequest 中的提交/清理

我实际上不喜欢视图模式中的会话,并且更喜欢让我的事务尽可能短地打开,而是将会话注入控制器。然后我在操作或服务中执行事务。我更喜欢这种对事务的细粒度控制,并使它们保持短暂,避免任何锁定问题。

于 2012-05-02T12:03:26.240 回答
1

我完全建议剥离交易内容

using (ISession Session = NhibernateSessionManager.GetContextSession())
{
  using (ITransaction Transaction = Session.BeginTransaction())
  {
   ...
  }
}

并将其移动到开始/结束请求中。这样for每个请求都有一个 UOW。您会话关闭的原因是因为using statement.

然后,您的开始请求代码可以是以下内容:-

var session = sessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
session.BeginTransaction();

和您的最终要求:-

var session = CurrentSessionContext.Unbind(sessionFactory);

if (session != null)
{
    if (session.Transaction.IsActive)
    {
        try
        {
            session.Transaction.Commit();
        }
        catch
        {
            session.Transaction.Rollback();
        }
    }
    session.Close();
}

我的 global.asax 中有这个

public static ISessionFactory SessionFactory { get; set; }

这在我的存储库中

    public ISession Session
    {
        get
        {
            return SessionFactory.GetCurrentSession();
        }
    }

现在我使用 IOC 将我的 sessionFactory 传递给我的存储库层,否则您将需要自己手动传递它。

于 2012-05-02T11:17:20.870 回答