我的任务是接管由第三方开发人员开发的现有ASP.NET MVC 2.0 Web应用程序,该开发人员不再提供任何帮助。需要在项目中添加一些功能,这需要将项目升级到.NET 4.5,这已经执行了。
MSSQL 2008 R2数据库访问的基础已经使用NHibernate 2.0.1.4000版以及Castle和FluentNHibernate实现。
这是我参与过的第一个使用NHibernate的项目,我遇到了一个让我难过的问题。在升级到.NET 4.5之前,问题不存在。
所有数据库操作都正常工作,除了一个。保存特定对象(
Opportunity
类型)到数据库(此对象直接映射到Opportunity数据库表)失败。在保存之前(在本例中为SQL UPDATE语句),该对象设置了新值。但是保存后,数据库中的记录始终具有旧值。
连接log4net以查看调试代码,显示记录确实已更新,但使用UPDATE语句中的旧值。
令人惊讶的是,Opportunity
对象最初使用相同的Save方法保存(尽管通过不同的操作方法),并且保存到数据库就好了。
所以我的问题是,会导致这种情况发生的原因是什么?因为我不是NHibernate专家,NHibernate版本是否与.NET 4.5完全不兼容?或者任何人都可以指出问题可能是什么?我很乐意展示任何代码,但由于有太多我需要知道什么。以下是首发:
Global.asax具有以下对NHibernate的引用:
private static void MvcApplication_BeginRequest(object sender, System.EventArgs e)
{
NHibernateSessionManager.Instance.BeginTransaction();
}
private static void MvcApplication_EndRequest(object sender, System.EventArgs e)
{
NHibernateSessionManager.Instance.CommitTransaction();
}
NHibernateSessionManager
类定义为(Opportunity
派生自DomainBase
):
public sealed class NHibernateSessionManager
{
private ISessionFactory sessionFactory;
private Configuration config;
#region Thread-safe, lazy Singleton
public static NHibernateSessionManager Instance
{
get
{
return Nested.nHibernateSessionManager;
}
}
private NHibernateSessionManager()
{
InitSessionFactory();
}
private class Nested
{
internal static readonly NHibernateSessionManager nHibernateSessionManager = new NHibernateSessionManager();
}
#endregion
private void InitSessionFactory()
{
var autoMappings = AutoPersistenceModel.MapEntitiesFromAssemblyOf<DomainBase>()
.Where(type =>
typeof(DomainBase).IsAssignableFrom(type) &&
type.IsClass &&
!type.IsAbstract)
.WithSetup(s =>
{
s.IsBaseType = type =>
type == typeof (DomainBase);
})
.UseOverridesFromAssemblyOf<OpportunityMappingOverride>()
.ConventionDiscovery.Add(DefaultLazy.AlwaysTrue())
.ConventionDiscovery.Add<CascadeAllHasOneConvention>()
.ConventionDiscovery.Add<CascadeAllHasManyConvention>()
.ConventionDiscovery.Add<CascadeAllReferenceConvention>();
sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005
.ConnectionString(c => c.FromConnectionStringWithKey("Default"))
.UseReflectionOptimizer()
.Cache(c => c.UseQueryCache().UseMininmalPuts().ProviderClass<SysCacheProvider>())
.ShowSql())
.Mappings(m => m.AutoMappings.Add(autoMappings))
.ExposeConfiguration(SetConfiguration)
.BuildSessionFactory();
}
private void SetConfiguration(Configuration cfg)
{
config = cfg;
}
public void RegisterInterceptor(IInterceptor interceptor)
{
ISession session = threadSession;
if (session != null && session.IsOpen)
{
throw new CacheException("You cannot register an interceptor once a Session has already been opened");
}
GetSession(interceptor);
}
public void GenerateSchema()
{
new SchemaExport(config).Execute(false, true, false, false);
}
public ISession GetSession()
{
return GetSession(null);
}
private ISession GetSession(IInterceptor interceptor)
{
ISession session = threadSession;
if (session == null)
{
if (interceptor != null)
{
session = sessionFactory.OpenSession(interceptor);
}
else
{
session = sessionFactory.OpenSession();
}
threadSession = session;
}
return session;
}
public void CloseSession()
{
ISession session = threadSession;
threadSession = null;
if (session != null && session.IsOpen)
{
session.Close();
}
}
public void BeginTransaction()
{
ITransaction transaction = threadTransaction;
if (transaction == null)
{
transaction = GetSession().BeginTransaction();
threadTransaction = transaction;
}
}
public void CommitTransaction()
{
ITransaction transaction = threadTransaction;
try
{
if (transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack)
{
transaction.Commit();
threadTransaction = null;
}
}
catch (HibernateException)
{
RollbackTransaction();
throw;
}
}
public void RollbackTransaction()
{
ITransaction transaction = threadTransaction;
try
{
threadTransaction = null;
if (transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack)
{
transaction.Rollback();
}
}
finally
{
CloseSession();
}
}
private static ITransaction threadTransaction
{
get
{
return (ITransaction)CallContext.GetData("THREAD_TRANSACTION");
}
set
{
CallContext.SetData("THREAD_TRANSACTION", value);
}
}
private static ISession threadSession
{
get
{
return (ISession)CallContext.GetData("THREAD_SESSION");
}
set
{
CallContext.SetData("THREAD_SESSION", value);
}
}
}
我希望我不会因为这个问题过于笼统而被击落。我花了一天时间试图弄清楚发生了什么,包括在线进行大量搜索。
答案 0 :(得分:2)
原来问题是,NHibernateSessionManager
类正在ITransaction
中存储其ISession
和System.Runtime.Remoting.Messaging.CallContext
个对象。
将其交换出来以存储HttpContext.Current.Items
集合中的对象解决了问题。
我发现this post暗示.NET 4.5处理CallContext
与以前的版本略有不同,这显然导致了我的问题。
因为NHibernateSessionManager
类在一个类库中,也被一些很少使用的控制台应用程序使用,所以我留下了CallContext
对象的后退,如下所示(不漂亮,并且那里可能是一个更好的选择,但为我工作[需要测试],因为我花了很长时间来使用远程调试来解决这个问题):
private static ITransaction threadTransaction
{
get
{
try
{
return (ITransaction)System.Web.HttpContext.Current.Items["THREAD_TRANSACTION"];
}
catch
{
return (ITransaction)CallContext.GetData("THREAD_TRANSACTION");
}
}
set
{
try
{
System.Web.HttpContext.Current.Items["THREAD_TRANSACTION"] = value;
}
catch
{
CallContext.SetData("THREAD_TRANSACTION", value);
}
}
}
private static ISession threadSession
{
get
{
try
{
return (ISession)System.Web.HttpContext.Current.Items["THREAD_SESSION"];
}
catch
{
return (ISession)CallContext.GetData("THREAD_SESSION");
}
}
set
{
try
{
System.Web.HttpContext.Current.Items["THREAD_SESSION"] = value;
}
catch
{
CallContext.SetData("THREAD_SESSION", value);
}
}
}