Search code examples
c#.net-corenhibernateruntime-errorfluent-nhibernate

How to make NHibernate 5.2.7 not have runtime exceptions with ASP.NET Core 3.1?


On a new project my team and I are working on, I'm exploring if NHibernate 5.2.7 / FluentNHibernate 2.1.2 are compatible with ASP.NET Core 3.1. I did some research on NHibernate 5.2.3, a slightly earlier version, and in that SO thread, they noted that NHibernate at this point should be .NET Core compatible.

With that being said, that does not appear to be the case.

Below is how we use NHibernate. We create sessions using this DataContext object (in a using block, of course, because ISessions are IDisposable!) and then use QueryOver to fetch or set data to our database.

However, when we attempt any DB operations, we get the following exception on the _sessionFactory.OpenSession() call:

Could not load type 'System.Runtime.Remoting.Messaging.CallContext' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
Inner exception: null
Stack trace: at NHibernate.Impl.SessionIdLoggingContext.get_SessionId()
  at NHibernate.Impl.SessionIdLoggingContext..ctor(Guid id)
  at NHibernate.Impl.AbstractSessionImpl.BeginContext()
  at NHibernate.Impl.AbstractSessionImpl..ctor(ISessionFactoryImplementor factory, ISessionCreationOptions options)
  at NHibernate.Impl.SessionImpl..ctor(SessionFactoryImpl factory, ISessionCreationOptions options)
  at NHibernate.Impl.SessionFactoryImpl.SessionBuilderImpl`1.OpenSession()
  at NHibernate.Impl.SessionFactoryImpl.OpenSession()
  at MyRadProject.Core.DataContext.GetSession() in C:\MyRadProject.Core\DataContext.cs:line 18
  at MyRadProject.Core.Repositories.UserRepository.GetUsers(Boolean includeInactive) in C:\MyRadProject.Core\Repositories\UserRepository.cs:line 23
  at MyRadProject.Web.Controllers.CommonController.GetUsers() in C:\MyRadProject.Web\Controllers\CommonController.cs:line 21
  at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
  at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
  at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()

As far as my team can tell, this exception previously occurred in earlier versions of .NET Core due to certain namespaces or classes that weren't provided for in the .NET Core (e.g. System.Configuration.ConfigurationManager) To the shared credit of .NET Core and NHibernate these issues seem to be much mitigated.

I double-checked that mscorlib was referenced in the library project that our data-access code exists in ("MyAwesomeProject.Core"). In Visual Studio 2019, I didn't see such a reference; when I attempted to manually reference mscorlib, I got a message, "mscorlib is automatically referenced by the build process."

All of this leads to my question:
Assuming that the SO thread above is accurate in stating that NHibernate 5.1.1+ is .NET Core-compatible, what else do I need to do to make it such that NHibernate 5.2.7 does not generate a runtime exception when I attempt any database operation?

DataContext.cs

public class DataContext
{
    private static ISessionFactory _sessionFactory;
    private static bool _startupComplete;

    private static readonly object _locker = new object();

    public static ISession GetSession()
    {
        EnsureStartup();
        ISession session = _sessionFactory.OpenSession();  // EXCEPTION occurs here!
        session.BeginTransaction();
        return session;
    }

    public static void EnsureStartup()
    {
        if (!_startupComplete)
        {
            lock (_locker)
            {
                if (!_startupComplete)
                {
                    PerformStartup();
                    _startupComplete = true;
                }
            }
        }
    }

    private static void PerformStartup()
    {
        InitializeSessionFactory();
    }

    private static void InitializeSessionFactory()
    {
        Configuration configuration = BuildConfiguration();
        _sessionFactory = configuration.BuildSessionFactory();
    }

    public static Configuration BuildConfiguration()
    {
        var configuration = new Configuration().Configure();
        return Fluently.Configure(configuration)
            .Mappings(cfg => cfg.FluentMappings.AddFromAssembly(typeof(DataContext).Assembly))
            .BuildConfiguration();
    }

    // There are other methods below this point that aren't pertinent to the question.
}

Solution

  • It seems you are using Full .NET Framework version of the library as CallContext class is not used in .NET Core/.NET Standard version of NHibernate (AsyncLocal is used instead): https://github.com/nhibernate/nhibernate-core/blob/c0f50429722b4e061a0c6b40b720db17d33fc85e/src/NHibernate/Impl/SessionIdLoggingContext.cs#L60-L65

    So to fix your issue try to use .NET Core or .NET Standard version of the library.