views:

300

answers:

1

Is it possible for different NHibernate sessions to share one 1st level cache? I`ve tried to implement it using interceptors and listeners. All works fine except Session.Evict().

public class SharedCache :
    EmptyInterceptor,
    IFlushEntityEventListener,
    ILoadEventListener,
    IEvictEventListener,
    ISharedCache {
    [ThreadStatic]
    private readonly Dictionary<string, Dictionary<object, object>> cache;

    private ISessionFactory factory;

    public SharedCache() {
        this.cache = new Dictionary<string, Dictionary<object, object>>();
    }

    public override object Instantiate(string clazz, EntityMode entityMode, object id) {
        var entityCache = this.GetCache(clazz);
        if (entityCache.ContainsKey(id))
            return entityCache[id];

        var entity = Activator.CreateInstance(Type.GetType(clazz));
        this.factory.GetClassMetadata(clazz).SetIdentifier(entity, id, entityMode);
        return entity;
    }

    private Dictionary<object, object> GetCache(string clazz) {
        if (!cache.ContainsKey(clazz))
            cache.Add(clazz, new Dictionary<object, object>());

        return cache[clazz];
    }

    public void Configure(Configuration config) {
        config.SetInterceptor(this);
        config.SetListener(ListenerType.FlushEntity, this);
        config.SetListener(ListenerType.Load, this);
        config.SetListener(ListenerType.Evict, this);
    }

    public void Initialize(ISessionFactory sessionFactory) {
        this.factory = sessionFactory;
    }

    public void OnFlushEntity(FlushEntityEvent ev) {
        var entry = ev.EntityEntry;

        var entityCache = this.GetCache(ev.EntityEntry.EntityName);

        if (entry.Status == Status.Deleted) {
            entityCache.Remove(entry.Id);
            return;
        }

        if (!entry.ExistsInDatabase && !entityCache.ContainsKey(entry.Id))
            entityCache.Add(entry.Id, ev.Entity);
    }


    public void OnLoad(LoadEvent ev, LoadType loadType) {
        var entityCache = this.GetCache(ev.EntityClassName);

        if (entityCache.ContainsKey(ev.EntityId))
            ev.Result = entityCache[ev.EntityId];
    }

    public void OnEvict(EvictEvent ev) {
        var entityName = ev.Session.GetEntityName(ev.Entity);
        var entityCache = this.GetCache(entityName);
        var id = ev.Session.GetIdentifier(ev.Entity);

        entityCache.Remove(id);
    }

}
A: 

No 1rst level or session cache cannot be shared if you want to share a cache you should use the 2nd level cache which goes with the session factory - see the docs

you have to be careful as the cache will not be invalidated if the data is changed outside of nhibernate sessions eg via triggers or some other client - or another instance of your code running on another machine

Richard