alastairtree / LazyCache

An easy to use thread safe in-memory caching service with a simple developer friendly API for c#
https://nuget.org/packages/LazyCache
MIT License
1.72k stars 159 forks source link

Cannot dispose of cache #179

Open jdurnil opened 2 years ago

jdurnil commented 2 years ago

Describe the bug I have tried everything to get rid of cache including renaming item, providing a new memorycacheprovider etc.. GetorAdd still pulls my old cache no matter what I do even after disposing of provider and creating new provider. How is this possible.

To Reproduce just try to get new cache under any circumstance

Expected behavior A clear and concise description of what you expected to happen.

** Framework and Platform

Additional context Add any other context about the problem here.

alastairtree commented 2 years ago

Hello,

For someone to help debug your problem you would need to provide some example of the code not working. A minimnal reproduction in a console app would make it much easier for others to be able to help. You may also find Stack Overflow is a better source of help for this type of issue as it will get many more eyeballs than raising tickets here.

In the mean time I suggest you check out the docs on deleting items from the cache and on emptying the entire cache.

Thanks

jeffhehe commented 1 year ago

I have a similar issue when I register LazyCache in dependency injection. If I dispose the cache with this instruction emptying the entire cache, I get ObjectDisposedException: Cannot access a disposed object. Object name: 'Microsoft.Extensions.Caching.Memory.MemoryCache' when I visit pages that uses the cache.

touchofevil-dev commented 1 year ago

I was able to reproduce this in a test.

[Test]
public void DisposalFailure()
{
    var container = new ServiceCollection();
    container.AddLazyCache();
    var provider = container.BuildServiceProvider();

    var cache = provider.GetRequiredService<IAppCache>();
    cache.CacheProvider.Dispose();

    var cache2 = provider.GetRequiredService<IAppCache>();
    Assert.Throws<ObjectDisposedException>(() => cache2.CacheProvider.Get("Test"));
    Assert.Throws<ObjectDisposedException>(() => cache2.Get<dynamic>("Test"));
}

I think this issue is particularly seen when using DI, and seems to be caused by how objects are being disposed.

ICachingProvider is disposable, however it is registered as singleton. This means when someone does cachingService.CacheProvider.Dispose(), CacheProvider will now be holding onto a disposed singleton instance, and all components interacting with cache will have a failure.

I do wonder if ICachingProvider should even be disposable. IMemoryCache is a dependency and our class object should not be responsible for disposing a dependency, since like we see in this case, that same dependency might be used elsewhere. Also, It is internally calling IMemoryCache.Dispose(), which doesn't seem to be doing much itself except suppressing a finalizer (though that is a different library/problem altogether), so I am not sure if we really have an issue if we don't explicitly dispose the IMemoryCache.

This might however be a breaking change and impact on users who're clearing cache by disposing caching provider directly, or have their own implementation of the ICachingProvider. @alastairtree - Please let us know if that sounds okay to you, I'd be happy to get this done with a PR.