什么会导致NHibernate的Save方法无声地失败?

时间:2014-12-12 20:25:25

标签: asp.net-mvc nhibernate

我的任务是接管由第三方开发人员开发的现有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);
        }
    }
}

我希望我不会因为这个问题过于笼统而被击落。我花了一天时间试图弄清楚发生了什么,包括在线进行大量搜索。

1 个答案:

答案 0 :(得分:2)

原来问题是,NHibernateSessionManager类正在ITransaction中存储其ISessionSystem.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);
            }
        }
    }