我有一个使用绝对最新版本(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;
}
答案 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中提交/清除
我实际上不喜欢视图模式中的会话,而是希望尽可能缩短我的事务处理,而是将会话注入控制器。然后,我在操作或服务中执行事务。我更喜欢对事务进行细粒度控制,并使它们保持短暂存在,避免任何锁定问题。