Search code examples
nhibernate

NHibernate: How to get entity instance from session cache?


I start the session at the start of unit of work and close at the end of it. Unit of work is spreed across multiple methods.

In one method, I load the entity using Get method. So this sits in session cache. Entity instance is local to the method. So when method scope ends, entity instance is not accessible. But entity is still in session cache.

Now, second method creates new instance of entity and tries to delete it. This throws NonUniqueObjectException as expected.

Following is the solution I can imagine, but not able to implement:

public void Delete<T>(T instance) where T : BaseEntity
{
    try
    {
        nhSession.Delete(instance);
    }
    catch(NonUniqueObjectException)
    {
        T instanceFromCache = GetInstanceFromCache<T>(instance);
        nhSession.Evict(instanceFromCache);
        nhSession.Delete(instance);
    }
}

If I can get the entity instance from session cache somehow, I can Evict it and hopefully the problem will be solved. But I am not able to implement my imaginary GetInstanceFromCache method.

I tried using nhSession.Get, but that is not helpful in my scenario. Primary key column name in my database is NOT "id" and also it is not same across tables. In one table, it is "Field1" and in other it is "Field2". So I cannot use something like nhSession.Get(instance.Id). My Delete<T>(T instance) method receives entity instance to delete as parameter. It does not receive value of primary key to delete.

For more info, please refer my other question. That question discusses about UPDATE issue and how I fixed it; but scenario is similar.

Edit 1

Answer by "@Ricardo Peres" does not work as is, but I modified his code a little.

public static TEntity GetInstanceFromCache<TEntity>(this ISession nhSession, object instance) where TEntity : BaseEntity
{
    var sessionImpl = nhSession.GetSessionImplementation();
    foreach(BaseEntity baseEntity in sessionImpl.PersistenceContext.EntityEntries.Keys)
    {
        if(baseEntity is TEntity)
        {
            TEntity instanceFromCache = (TEntity)baseEntity;
            if(nhSession.GetIdentifier(instanceFromCache) == nhSession.GetIdentifier(instance))
                return baseEntity as TEntity;
        }
    }
    return null;
}

The call nhSession.GetIdentifier(instance) throws an exception TransientObjectException ("The instance was not associated with this session") which is expected. This is because instance is unknown to nhSession. Any way to get identifier of entity that is NOT associated with session?


Solution

  • You need to get hold of the PersistenceContext, like this:

    using System.Linq;
    using NHibernate;   
    
    public static T GetInstanceFromCache<T>(this ISession session, object key) where T : class
    {
        var entity = session.GetSessionImplementation().PersistenceContext.EntitiesByKey.SingleOrDefault(x => x.Value is T && x.Key.Identifier == key);
        return entity as T;
    }