NHibernate Transaction.Commit自动关闭Session

时间:2012-04-30 05:06:35

标签: nhibernate session transactions

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

我知道我没有这样做,因为对Iession.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()之后立即关闭的函数。每个内部函数检索当前会话,然后处理保存调用。

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

3 个答案:

答案 0 :(得分:4)

会话将在AddVideo方法中处理,因为您正在使用using Statement进行会话。

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

}

答案 1 :(得分:1)

我完全建议删除交易内容

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

并将其移至开始/结束请求中。这样,每个请求都有一个UOW for。会话因为using statement而关闭IS的原因。

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

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传递给我的存储库层,否则你需要手动传递它。

答案 2 :(得分:1)

提交交易将结束该会话。

将您的事务开始移至context_BeginRequest并在context_EndRequest中提交/清除

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