.net核心InMemoryCache引发错误

时间:2020-10-07 14:01:02

标签: c# asp.net-core caching in-memory

我正在使用InMemoryCache,如下所示。我没有通过startup.cs进行配置。这里的问题是它在托管的docker映像中引发以下错误,而本地未遇到此错误。

[Error] ConnectionId:0 RequestPath:/BuildSpec/RetrieveFor RequestId:1:0, SpanId:|aaaaa-ccccccc., a-415ea81b37b05aaa4f3, ParentId: Controller.RetrieveFor (core.web) => Microsoft.EntityFrameworkCore.Query: An exception occurred while iterating over the results of a query for context type 'act.core.data.ActDbContext'.
System.InvalidOperationException: A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
   at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()

但是我们将数据库上下文注入到构造函数中。粘贴以下我正在使用的代码段。

 private static readonly MemoryCache _cache = new MemoryCache(new MemoryCacheOptions
        {
            SizeLimit = 10
        });

        private async Task<IQueryable<SoftwareComponent>> GetOrCreateSoftwareComponent()
        {
            const string key = "xyz";
            if (!_cache.TryGetValue(key, out IQueryable<xyz> xyz))
            {
                softwareComponents = await Task.Run(() => _ctx.xyztable.AsNoTracking()
                    .Include(a => a.SoftwareComponentEnvironments).AsNoTracking());

                var cacheEntryOptions = new MemoryCacheEntryOptions()
                    .SetPriority(CacheItemPriority.High)
                    .SetSize(1)
                    .SetSlidingExpiration(TimeSpan.FromMinutes(30))
                    .SetAbsoluteExpiration(TimeSpan.FromMinutes(60));

                _cache.Set(key, xyz, cacheEntryOptions);
            }
            return softwareComponents;
        }

这里的问题是,我没有在这里得到错误。为什么是MySQL错误。 注意:如果我重新部署相同的代码,则有一天可以解决该问题。但是错误一次又一次地回来。

1 个答案:

答案 0 :(得分:0)

您在此行遇到错误:

 softwareComponents = await Task.Run(() => _ctx.xyztable.AsNoTracking()
                    .Include(a => a.SoftwareComponentEnvironments).AsNoTracking());

Task.Run>将指定的工作排队以在ThreadPool上运行,并返回该工作的任务或任务句柄。

这意味着其中的委托将在单独的线程上运行。

因此,如果您请求其他一些缓存数据(甚至在第一个完成之前,甚至是相同的),则另一个线程将启动并使用相同的上下文。 EF中不允许这样做,因此会出现错误。

简单的解决方法是使用锁

        private readonly object lockObject = new object();
        private async Task<IQueryable<SoftwareComponent>> GetOrCreateSoftwareComponent()
        {
          const string key = "xyz";
          lock (lockObject)
          {
            if (!_cache.TryGetValue(key, out IQueryable<xyz> xyz))
            {
                softwareComponents = await Task.Run(() => _ctx.xyztable.AsNoTracking()
                    .Include(a => a.SoftwareComponentEnvironments).AsNoTracking());

                var cacheEntryOptions = new MemoryCacheEntryOptions()
                    .SetPriority(CacheItemPriority.High)
                    .SetSize(1)
                    .SetSlidingExpiration(TimeSpan.FromMinutes(30))
                    .SetAbsoluteExpiration(TimeSpan.FromMinutes(60));

                _cache.Set(key, xyz, cacheEntryOptions);
            }
          }
          return softwareComponents;
        }

您每次都可以提供一个新的上下文,但是如果没有某种同步,您可能会对相同的值进行多个查询。