c# - .net core InMemoryCache 抛出错误
问题描述
我正在使用 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()
但是我们将 DB 上下文注入到构造函数中。粘贴我在这里使用的以下代码片段。
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 错误。注意:如果我重新部署相同的代码,问题会在某一天得到解决。但是错误一次又一次地出现。
解决方案
您在此处收到此行的错误:
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;
}
您可以每次都提供一个新的上下文,但如果没有某种同步,您可能会针对同一个值进行多次查询。
推荐阅读
- python - 如何将 JSON 的一部分提取为另一个 JSON,并在 Python 上将它们分开几个 JSON 文件
- azure-devops - 如何在 AzureDevops 中使用工件从一个作业构建管道到另一个作业
- kotlin - 从变量传递类型
- java - Java 和 .NET C# 的 XSD 1.1 验证?
- oracle - Oracle 多租户 - 在另一个容器中通过 DBMS_SQL 执行 PL/SQL
- ios - Collection View Xib Cell Bug 中的表视图
- c++ - 为什么我不能约束一个概念
- python - 'IndexError:索引 4 超出轴 1 的范围,大小为 4'
- vb.net - VB.net 如何组合列表和类?
- python - pyqtgraph 主窗体内的绘图(qt 设计器)