7

Everyone knows that there is cache in session. This cache generally could be cleared by 2 methods:

  1. Session.Evict
  2. Session.Clear

Second method removes all cache not only for single entry.

I have business method. It receives id of large object (from aspx site) or sometimes several ids. And do native sql operation in database (using sql-query with complex logic to not load all data in C#). Then I need to invalidate cache. So every potential load of object goes without cache direct from database.

Unfortunately evict accepting only objects. Also its implementation DefaultEvictEventListener has clear separation in code path - separate for proxy and not proxied classes. I have tried simply to create entity, fill id manually and pass it to Evict. This will not works. As I understand Evict by not proxied class use GetHashCode to find and remove object from cache. So if I am not overriding it it will not works. I have a lot native sql batch operations so overriding all GetHashcode in all entities objects will create a lot of work. Also I am not sure is this case removes proxies from cache or no. Update: As far as I have tried for me overriding GetHashCode also not helped. StatefulPersistenceContext.RemoveEntry not found entity because it uses RuntimeHelpers.GetHashCode. So this solution is not even possible

Using sources of NHibernate I have produced following solution:

public static class NHSessionHelper: DefaultEvictEventListener
 public static void RemoveEntityFromCache(this ISession session, Type type, object entityId)
    {
        ISessionImplementor sessionImpl = session.GetSessionImplementation();
        IPersistenceContext persistenceContext = sessionImpl.PersistenceContext;
        IEntityPersister persister = sessionImpl.Factory.GetEntityPersister(type.FullName);

        if (persister == null)
        {
            return;
        }

        EntityKey key = new EntityKey(entityId, persister, sessionImpl.EntityMode);
        persistenceContext.RemoveProxy(key);

        object entity = persistenceContext.RemoveEntity(key);
        if (entity != null)
        {
            EntityEntry e = persistenceContext.RemoveEntry(entity);
            DoEvict(entity, key, e.Persister, (IEventSource)sessionImpl);
        }
    }

It just uses part of NHibenate implementation. But it seem to me not good idea to duplicate code. May be anyone have other ideas?

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
Yauhen.F
  • 2,382
  • 3
  • 19
  • 25

2 Answers2

9

If you are sure that the object is in the cache, Session.Get(id) will not hit the db. It's probably easiest to do that and then Evict the object you get back:

Model m = Session.Get(id);
Session.Evict(m);

Edit

It is not clear to me if you are talking about the first-level cache or the second-level cache. The above will evict something from the first-level cache. To evict from the second-level cache, use the evict method on SessionFactory.

Edit in response to comment

In that case, you might try Session.Load:

Model m = Session.Load(id);
Session.Evict(m);

If m is in the cache, Session.Load will return the instance which you can evict. If not it returns a proxy (no db hit). My tests suggest that Session.Evict will not throw if you try to evict the proxy, so this should work.

Gabe Moothart
  • 31,211
  • 14
  • 77
  • 99
  • This is the right thing to do. Get returns a proxy to the model and sets the id – jonnii Nov 05 '10 at 15:26
  • I am speaking about first level cache. I am not sure is it in cache or no. SOmetimes it is sometimes no. In any of this cases I want to clear it from cache. If it is not in cache you code could do real hit to database. So I want identical code for both cases. So data manipulate operation just make sure that there are no data in cache. – Yauhen.F Nov 05 '10 at 15:29
  • Regarding last "Edit in response to comment". If it will not hit database inside Evict when manipulating and no exception for entity not in cache I would say your solution is best. – Yauhen.F Nov 05 '10 at 15:50
  • @Yauhen it doesn't seem to case a db hit in my tests. Verify that this works for you, and if so don't forget to mark my answer as accepted! – Gabe Moothart Nov 05 '10 at 16:03
  • `Session.Load` **does** go to the DB if the object isn't already on the cache, it just avoid the useless query when it's cached. The proxy it returns is initialized with data from this query. – Alejandro Aug 28 '15 at 11:57
3

It sounds like you could use a Stateless session for this and not bother with cache at all.

tom.dietrich
  • 8,219
  • 2
  • 39
  • 56
  • I was thinking about stateless sessions. It is good idea. But I have too many code written (something like 152 business methods). And if I change all to stateless session. I will to many code to test. At least as all operations will use the same type of session. It seem to me it is not go idea when some DAO will use one type of sesison and others will use another. – Yauhen.F Nov 05 '10 at 15:35
  • 1
    The basic idea is to use normal sessions when you are going to be navigating relationships and updating the persisted objects, and stateless sessions when you simply want to run a query and return a POCO object. – tom.dietrich Nov 05 '10 at 15:44