What will cause NHibernate's persistence method to fail?

I was tasked with taking over an existing ASP.NET MVC 2.0 web application that was developed by a third party developer who is no longer there to provide any assistance. It needed to add some functionality to the project, which required updating the project to .NET 4.5, which was done.

The sites behind the MSSQL 2008 R2 database access were implemented using NHibernate version 2.0.1.4000 as well as Castle and FluentNHibernate.

This is the first project I have participated in that used NHibernate and I ran into an issue that puzzled me a lot. The issue did not exist until the upgrade to .NET 4.5.

All database operations work fine except for one. Saving a specific object ( Opportunity

) to the database (this object maps directly to the Opportunity database table) fails. Before saving (in this case, the SQL UPDATE statement), the object has new values. But after saving in the database, the old values ​​are always saved.

Connecting log4net to view the debug code shows that the entry is indeed updated, but uses the old values ​​in the UPDATE statement.

Surprisingly, an object Opportunity

is stored differently with the same save method (albeit with a different action method), and it is very convenient to save to the database.

So my question is, what could have happened? Being that I am not an NHibernate expert, is it that the NHibernate version is simply not compatible with .NET 4.5? Or can someone point out a pointer to what might be the problem? I'm happy to show any code, but since I really need to know what. Below is the starter:

Global.asax has the following NHibernate references:

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

      

The class is NHibernateSessionManager

defined as ( Opportunity

derived from 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);
        }
    }
}

      

I hope I don't get killed because this question is too general. I spent a whole day trying to figure out what was going on, including an extensive Internet search.

+3


source to share


1 answer


It turned out the problem was that the class NHibernateSessionManager

stores the object ITransaction

and ISession

in System.Runtime.Remoting.Messaging.CallContext

.

Replacing it to store objects in the collection HttpContext.Current.Items

fixed the issue.

I found this post , which implies that .NET 4.5 handles CallContext

slightly differently from previous versions, which obviously caused my problem.



Since the class NHibernateSessionManager

was in a class library that was also used by a few infrequently used console applications, I left a backup of the object CallContext

as shown below (not very much, and maybe better, but worked for me [subject to testing] as I wasted a lot of time to deal with remote debugging):

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

      

+2


source







All Articles