为参数的每个变体缓存 EF Core Complex 查询
EF Core Complex query gets cached for each variation of parameters
我有一个复杂的查询,似乎导致我的应用程序内存泄漏。
据我了解,查询结果已缓存,因此无需在每次执行查询时都进行此处理。 https://docs.microsoft.com/en-us/ef/core/querying/how-query-works
但它看起来像是为每个登录系统的用户缓存查询。从内存转储来看,我们似乎有数千个针对同一查询的已编译查询缓存对象。
查询如下所示。
public async Task<IList<EmployeeInboxMessage>> GetEmployeeMessagesAsync(long employeeId)
{
return await (from message in this.Repository.Set
join userStep in this.Repository.Context.Set<UserWorkflowHeaderStep>() on message.UserWorkflowStepId equals userStep.UserWorkflowStepId into userSteps
from userStep in userSteps.DefaultIfEmpty()
join acceptedStep in this.Repository.Context.Set<CompanyWorkflowStep>() on userStep.AcceptedStepId equals acceptedStep.WorkflowStepId into acceptedSteps
from acceptedStep in acceptedSteps.DefaultIfEmpty()
join rejectedStep in this.Repository.Context.Set<CompanyWorkflowStep>() on userStep.RejectedStepId equals rejectedStep.WorkflowStepId into rejectedSteps
from rejectedStep in rejectedSteps.DefaultIfEmpty()
let step =
message.InboxEntryType == InboxEntryType.Claims ||
message.InboxEntryType == InboxEntryType.AdvancedLeave ||
message.InboxEntryType == InboxEntryType.ChangeRequest
? new WorkflowHeaderStep
{
WorkflowItem = userStep.WorkflowItem,
AcceptedStepId = userStep.AcceptedStepId,
AcceptedStep = acceptedStep == null ? null : new WorkflowStep
{
OnApprovalActionId = acceptedStep.OnApprovalActionId,
OnRejectionAction = acceptedStep.OnRejectionAction,
OrderNumber = acceptedStep.OrderNumber
},
RejectedStepId = userStep.RejectedStepId,
RejectedStep = rejectedStep == null ? null : new WorkflowStep
{
OnApprovalActionId = rejectedStep.OnApprovalActionId,
OnRejectionAction = rejectedStep.OnRejectionAction,
OrderNumber = rejectedStep.OrderNumber
}
}
: null
let employeeName = string.IsNullOrWhiteSpace(message.OBOEmployee.PreferredName)
? message.OBOEmployee.FullName
: message.OBOEmployee.PreferredName + " " + message.OBOEmployee.LastName
where message.EmployeeId == employeeId
orderby message.EffectiveDate
select new EmployeeInboxMessage
{
Message = message,
UserStep = step,
UserId = message.UserId,
UserName = message.User.FullName,
EmployeeName = message.OBOEmployeeId.HasValue
? employeeName
: message.User.FullName,
RelatedPrimaryKey =
message.InboxEntryType == InboxEntryType.Claims ||
message.InboxEntryType == InboxEntryType.AdvancedLeave ||
message.InboxEntryType == InboxEntryType.ChangeRequest
? userStep.RelatedPrimaryKey
: message.UserWorkflowStepId!.Value,
StartUserCompanyId = message.StartUser.CompanyId
}).ToListAsync();
}
看起来您的查询对于 EF 创建单个 CompiledQueryCache 对象来说太复杂了,它正在为您传入的每个“employeeId”添加一个缓存版本。从外观上看,已经有 27804 个版本。
如果您重组查询,那么它可能会解决您的问题。尝试删除“let step = ...”和“let employeeName = ...”。最坏的情况是您需要创建原始 SQL 或视图。
没有很多文档解释 EF 如何创建查询缓存(我找不到任何文档)。你现在可能永远不会知道你的问题到底是什么,除非一些 EF 的创建者提供了一些输入或者你总是可以通过他们的代码。
如果您在更改查询后仍然遇到问题,最好使用 efcore 记录问题 https://github.com/dotnet/efcore
我有一个复杂的查询,似乎导致我的应用程序内存泄漏。
据我了解,查询结果已缓存,因此无需在每次执行查询时都进行此处理。 https://docs.microsoft.com/en-us/ef/core/querying/how-query-works
但它看起来像是为每个登录系统的用户缓存查询。从内存转储来看,我们似乎有数千个针对同一查询的已编译查询缓存对象。
查询如下所示。
public async Task<IList<EmployeeInboxMessage>> GetEmployeeMessagesAsync(long employeeId)
{
return await (from message in this.Repository.Set
join userStep in this.Repository.Context.Set<UserWorkflowHeaderStep>() on message.UserWorkflowStepId equals userStep.UserWorkflowStepId into userSteps
from userStep in userSteps.DefaultIfEmpty()
join acceptedStep in this.Repository.Context.Set<CompanyWorkflowStep>() on userStep.AcceptedStepId equals acceptedStep.WorkflowStepId into acceptedSteps
from acceptedStep in acceptedSteps.DefaultIfEmpty()
join rejectedStep in this.Repository.Context.Set<CompanyWorkflowStep>() on userStep.RejectedStepId equals rejectedStep.WorkflowStepId into rejectedSteps
from rejectedStep in rejectedSteps.DefaultIfEmpty()
let step =
message.InboxEntryType == InboxEntryType.Claims ||
message.InboxEntryType == InboxEntryType.AdvancedLeave ||
message.InboxEntryType == InboxEntryType.ChangeRequest
? new WorkflowHeaderStep
{
WorkflowItem = userStep.WorkflowItem,
AcceptedStepId = userStep.AcceptedStepId,
AcceptedStep = acceptedStep == null ? null : new WorkflowStep
{
OnApprovalActionId = acceptedStep.OnApprovalActionId,
OnRejectionAction = acceptedStep.OnRejectionAction,
OrderNumber = acceptedStep.OrderNumber
},
RejectedStepId = userStep.RejectedStepId,
RejectedStep = rejectedStep == null ? null : new WorkflowStep
{
OnApprovalActionId = rejectedStep.OnApprovalActionId,
OnRejectionAction = rejectedStep.OnRejectionAction,
OrderNumber = rejectedStep.OrderNumber
}
}
: null
let employeeName = string.IsNullOrWhiteSpace(message.OBOEmployee.PreferredName)
? message.OBOEmployee.FullName
: message.OBOEmployee.PreferredName + " " + message.OBOEmployee.LastName
where message.EmployeeId == employeeId
orderby message.EffectiveDate
select new EmployeeInboxMessage
{
Message = message,
UserStep = step,
UserId = message.UserId,
UserName = message.User.FullName,
EmployeeName = message.OBOEmployeeId.HasValue
? employeeName
: message.User.FullName,
RelatedPrimaryKey =
message.InboxEntryType == InboxEntryType.Claims ||
message.InboxEntryType == InboxEntryType.AdvancedLeave ||
message.InboxEntryType == InboxEntryType.ChangeRequest
? userStep.RelatedPrimaryKey
: message.UserWorkflowStepId!.Value,
StartUserCompanyId = message.StartUser.CompanyId
}).ToListAsync();
}
看起来您的查询对于 EF 创建单个 CompiledQueryCache 对象来说太复杂了,它正在为您传入的每个“employeeId”添加一个缓存版本。从外观上看,已经有 27804 个版本。
如果您重组查询,那么它可能会解决您的问题。尝试删除“let step = ...”和“let employeeName = ...”。最坏的情况是您需要创建原始 SQL 或视图。
没有很多文档解释 EF 如何创建查询缓存(我找不到任何文档)。你现在可能永远不会知道你的问题到底是什么,除非一些 EF 的创建者提供了一些输入或者你总是可以通过他们的代码。
如果您在更改查询后仍然遇到问题,最好使用 efcore 记录问题 https://github.com/dotnet/efcore