为参数的每个变体缓存 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