5

I'm trying to put a subset of db-data in an IMemoryCache, but the 2nd time I call the application, I get an error:

ObjectDisposedException: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances. Object name: 'WebDbContext'.

My Code snippet:

    public class ArticleRepository : IArticleRepository
    {
        private readonly WebDbContext _WebDbContext;
        private readonly IMemoryCache _cache;

        public ArticleRepository(WebDbContext WebDbContext, IMemoryCache cache)
        {
            _WebDbContext = WebDbContext;
            _cache = cache;
        }

        public IQueryable<Articles> WebshopArticles
        {
            get
            {
                return _cache.GetOrCreate("WebshopArticles", entry =>
                {
                    entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
                    return _WebDbContext.Article.Include(s => s.Details);
                });                
            }
        } 

        public IQueryable<Articles> GetArticles(string category)
        {
            return WebshopArticles.FirstOrDefault(s => s.Category == Category);
        }
    }

It looks like DBContext is disposed after the first time I put it in cache. How can i handle this?

CribAd
  • 452
  • 6
  • 19
  • Just FYI, closely related posting: https://stackoverflow.com/questions/55944381/cannot-access-a-disposed-object-with-memorycache – crazyTech Jun 21 '22 at 11:45

1 Answers1

12

You're using dependency injection to get an instance of your WebDbContext through your constructor. ASP.NET Core does this by initiating a WebDbContext object for you and injecting it into the constructor call when it creates an instance of your repository class.

But that WebDbContext object is only available for the life of the current HTTP request. Once that HTTP request is complete, ASP.NET Core gets rid of it. That's why you see it disposed.

Update: I see what you're doing. The problem is here:

return _WebDbContext.Article.Include(s => s.Details);

That does not cache the data. That caches the query (IQueryable). The query doesn't get executed until you enumerate that (loop through it). This is refered to as "lazy loading". So your GetArticles actually performs the query again each time it's called.

The first time you use it (in the same HTTP request you cached it), it works. But when you use it the second time, the context is disposed and the query can't be executed.

You need to force it to execute the query right away. An easy way is to call ToList():

return _WebDbContext.Article.Include(s => s.Details).ToList();

You'll need to change the property type to IEnumerable too.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • But caching the articles is faster then calling the db every time I need the same set of articles? Or maybe a better example: when I create a navigation based on some data in the db, it feels better to cache the data because the data doesn't change very often. – CribAd Oct 29 '18 at 14:16
  • @CribAd for that you need to cache the data, not the context, but only if it's data that can be accessed safely throughout the lifetime of the application (doesn't change). For changing data caching the context would slow things down as it would reduce concurrency. – Jon Hanna Oct 29 '18 at 14:18
  • Ok, things are getting clearer to me. I wnat to put some data in the cache, and I thought **_WebDbContext.Article.Include(s => s.Details);** will return data, but it actually returns a context, right? How can I get the data in the cache then? – CribAd Oct 29 '18 at 14:21
  • I added an update to my answer to show you how to cache the data and not the query. – Gabriel Luci Oct 29 '18 at 14:25
  • 1
    Thanks a lot, that's my missing link! – CribAd Oct 29 '18 at 14:29
  • But note that getting the data inside a property getter is not the best way. It'll work of course. But, for example, you can't use the async method (`ToListAsync()`) which is handy to use in ASP.NET for performance. [There are other reasons too.](https://stackoverflow.com/questions/6682902/why-should-a-c-sharp-property-getter-not-read-from-a-db) The best way is to use a method to get the data and cache it and make sure that method is called before the data is used. – Gabriel Luci Oct 29 '18 at 14:33
  • You could even just convert `WebshopArticles` from a property to an async method. – Gabriel Luci Oct 29 '18 at 14:39
  • Just FYI, closely related posting: https://stackoverflow.com/questions/55944381/cannot-access-a-disposed-object-with-memorycache – crazyTech Jun 21 '22 at 11:45