Stumped: Custom NHibernate Session management gives "Specified cast is not valid" exception (C#)

1.2k views Asked by At

I am trying to implement some proper NHibernate session management in my console application, but running into some very weird problems. One moment everything seems to be working fine, but randomly, all of a sudden it starts throwing an "Specified cast is not valid" exception when trying to fetch a specific object which seems to be pointing to a datetime value, but no more info is provided.

Now, I have eliminated the possibility of anything being wrong with my mappings, because I simply outcommented all fields except a string field and the primary key (which is a GUID). The exception keeps on being thrown nonetheless. Also, the object in question does not contain any child objects.

Instead I suspect there is something wrong with how I invoke my Session, so there is something in the cache of the Session that causes this problem for some nebulous reason.

I use this Util class for managing sessions:

public static class FcoNHibernateUtil
{
    private static readonly ISessionFactory sessionFactory = BuildSessionFactory();

    private static ISessionFactory BuildSessionFactory()
    {
        try
        {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration()
                .Configure()
                .AddAssembly("FcoPersistence")
                .AddAssembly("FcoLib")
                .AddAssembly("FargoLib")
                .BuildSessionFactory();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    public static ISessionFactory GetSessionFactory() 
    {
        return sessionFactory;
    }

    public static ISession GetCurrentSession()
    {
        if (!CurrentSessionContext.HasBind(GetSessionFactory()))
            CurrentSessionContext.Bind(GetSessionFactory().OpenSession());

        return GetSessionFactory().GetCurrentSession();
    }

    public static void DisposeCurrentSession()
    {
        ISession currentSession = CurrentSessionContext.Unbind(GetSessionFactory());

        if (currentSession != null)
        {
            currentSession.Close();
            currentSession.Dispose();
        }
    }
}

So at the beginning of every operation of my DataObjectManager classes, I get the session from a common method inherited from a superclass that fetches the session like so:

    public ISession GetSession()
    {
        return FcoNHibernateUtil.GetCurrentSession();   
    }

When all operations in one sweep of the console app (or a unit test) are done, I make sure the DisposeCurrentSession-method is called.

Is there anything that can be improved that may prevent these exceptions from coming?


EDIT

This is the stack trace:

 at NHibernate.Type.DateTimeType.IsEqual(Object x, Object y) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\DateTimeType.cs:line 93
 at NHibernate.Type.NullableType.IsEqual(Object x, Object y, EntityMode entityMode) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\NullableType.cs:line 368
 at NHibernate.Type.AbstractType.IsSame(Object x, Object y, EntityMode entityMode) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\AbstractType.cs:line 218
 at NHibernate.Type.AbstractType.IsDirty(Object old, Object current, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\AbstractType.cs:line 110
 at NHibernate.Type.NullableType.IsDirty(Object old, Object current, Boolean[] checkable, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\NullableType.cs:line 338
 at NHibernate.Type.TypeHelper.FindDirty(StandardProperty[] properties, Object[] currentState, Object[] previousState, Boolean[][] includeColumns, Boolean anyUninitializedProperties, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\TypeHelper.cs:line 227
 at NHibernate.Persister.Entity.AbstractEntityPersister.FindDirty(Object[] currentState, Object[] previousState, Object entity, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPersister.cs:line 3555
 at NHibernate.Event.Default.DefaultFlushEntityEventListener.DirtyCheck(FlushEntityEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEntityEventListener.cs:line 456
 at NHibernate.Event.Default.DefaultFlushEntityEventListener.IsUpdateNecessary(FlushEntityEvent event, Boolean mightBeDirty) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEntityEventListener.cs:line 187
 at NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultFlushEntityEventListener.cs:line 43
 at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 161
 at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\AbstractFlushingEventListener.cs:line 60
 at NHibernate.Event.Default.DefaultAutoFlushEventListener.OnAutoFlush(AutoFlushEvent event) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Event\Default\DefaultAutoFlushEventListener.cs:line 30
 at NHibernate.Impl.SessionImpl.AutoFlushIfRequired(ISet`1 querySpaces) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1145
 at NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1903
 at NHibernate.Impl.CriteriaImpl.List(IList results) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\CriteriaImpl.cs:line 265
 at NHibernate.Impl.CriteriaImpl.List[T]() in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Impl\CriteriaImpl.cs:line 276
 at FcoPersistence.GlobalLoginSettingsManager.GetGlobalLoginSettings(String ownIdent) in C:\Users\steemfe1\Documents\Work\Code\Epimetheus\branches\Refactoring\FcoPersistence\GlobalLoginSettingsManager.cs:line 189

And the method where it occurs:

    public GlobalLoginSettings GetGlobalLoginSettings(string ownIdent)
    {
        session = GetSession();//if(session == null) session = Factory.OpenSession();
        ITransaction tx = session.BeginTransaction();

        GlobalLoginSettings owner;

        try
        {
            ICriteria criteria = session.CreateCriteria(typeof (GlobalLoginSettings));

            criteria.Add(Restrictions.Eq("OWNIdent", ownIdent));

            var owners = criteria.List<GlobalLoginSettings>();  //<--- exception thrown

            tx.Commit();

            owner = owners.Count > 0 ? owners[0] : null;
        }
        catch (Exception e)
        {
            tx.Rollback();
            throw e;
        }
        return owner;
    }

EDIT2:

I am getting one clue and that is that this happens when the session is flushed automatically when getting the object. So it looks like the error is happening on the objects that were residing in the session from earlier transactions, but were not yet flushed. So the issue is lurking in the session until it is used again.

2

There are 2 answers

0
Fedor Alexander Steeman On BEST ANSWER

I am resorting to not re-using the Session, but the SessionFactory instead. That helped.

1
cremor On

The easiest way would be to extract the NHibernate source somewhere, point Visual Studio to it and check which value throws the exception when being casted to DateTime. Then go up some steps in the call stack to find the object where the value is in. That way you should be able to find the error.