[英]How to perform this EF Core nested date comparison query in SQL
Using .Net Core 2.1 and EF Core 2.1.1 and SQL Server 使用.Net Core 2.1和EF Core 2.1.1和SQL Server
I am trying to pull a list of Organizations
and their list of Communications
我正在尝试提取
Organizations
及其Communications
清单
I then want to limit it to those that have not had any Communications
in the last 6 months 然后,我想将其限制为最近6个月内未进行任何
Communications
的用户
Here are my skimmed down ViewModels: 这是我略过的ViewModels:
public class OrganizationViewModel
{
public Guid Id { get; set; }
public IEnumerable<CommunicationViewModel> CommunicationViewModels { get; set;
}
public class CommunicationViewModel
{
public Guid Id { get; set; }
public DateTime Date { get; set; }
public Guid OrganizationViewModelId { get; set; }
public OrganizationViewModel OrganizationViewModel { get; set; }
}
And here is my query: 这是我的查询:
DateTime sixMonthsAgo = DateTime.Today.AddMonths(-6);
int pageIndex = 1; // Would be passed in
int pageSize = 3;
IQueryable<OrganizationViewModel> query = _context.Organizations
.AsNoTracking()
.Select(organization => new OrganizationViewModel
{
CommunicationViewModels = organization.Communications.Select(communication => new CommunicationViewModel
{
Date = communication.Date
})
.OrderByDescending(communication => communication.Date)
.Take(1)
.ToList()
})
.Where(organization =>
(!searchViewModel.LimitToLastSixMonths ||
organization.CommunicationViewModels.Any(communication => communication.Date <= sixMonthsAgo)));
int totalAmount = await query.CountAsync();
List<OrganizationViewModel> items = await query
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
This gets me the expected results, but I can see in my logs that I'm performing this query on every record when I hit .CountAsync()
and . 这样可以得到预期的结果,但是当我点击
.CountAsync()
和时,可以在日志中看到对每条记录执行此查询。 Skip(..).Take(..)
: Skip(..).Take(..)
:
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM (
SELECT TOP(1) [comm].[Date]
FROM [Communications] AS [comm]
WHERE @_outer_Id = [comm].[OrganizationId]
ORDER BY [comm].[Date] DESC
) AS [t]
WHERE [t].[Date] <= @__sixMonthsAgo_0)
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
I'm also seeing these warnings when .CountAsync()
is called (slightly edited): 当
.CountAsync()
(稍作编辑)时,我也看到这些警告:
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression
Microsoft.EntityFrameworkCore.Query:警告:LINQ表达式
'where (False OrElse {from CommunicationViewModel cvm in {from Communication comm in value(..EntityQueryable'1[..Models.Communication]) orderby [comm].Date desc where ?= (Property([o], "Id") == Property([comm], "OrganizationId")) =? select new CommunicationViewModel() {Date = [comm].Date} => Take(1) => AsQueryable()} where ([cvm].Date <= __sixMonthsAgo_0) select [cvm] => Any()})'
could not be translated and will be evaluated locally.'where (False OrElse {from CommunicationViewModel cvm in {from Communication comm in value(..EntityQueryable'1[..Models.Communication]) orderby [comm].Date desc where ?= (Property([o], "Id") == Property([comm], "OrganizationId")) =? select new CommunicationViewModel() {Date = [comm].Date} => Take(1) => AsQueryable()} where ([cvm].Date <= __sixMonthsAgo_0) select [cvm] => Any()})'
无法翻译,将在本地进行评估。
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'Count()' could not be translated and will be evaluated locally.Microsoft.EntityFrameworkCore.Query:警告:LINQ表达式'Count()'无法翻译,将在本地进行评估。
And similar errors when the .Take(..).Skip(..)
is called: 以及调用
.Take(..).Skip(..)
时的类似错误:
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression
Microsoft.EntityFrameworkCore.Query:警告:LINQ表达式
same as above
could not be translated and will be evaluated locally.same as above
无法翻译,将在本地进行评估。 Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'Skip(__p_1)' could not be translated and will be evaluated locally.Microsoft.EntityFrameworkCore.Query:警告:LINQ表达式'Skip(__ p_1)'无法翻译,将在本地进行评估。
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'Take(__p_2)' could not be translated and will be evaluated locally.Microsoft.EntityFrameworkCore.Query:警告:LINQ表达式“ Take(__ p_2)”无法翻译,将在本地进行评估。
This does not happen when searchViewModel.LimitToLastSixMonths
is false 当
searchViewModel.LimitToLastSixMonths
为false时,不会发生这种情况
Any suggestions on how I can rewrite my query to not perform that query locally on each record? 关于如何重写查询以不对每个记录在本地执行该查询的任何建议?
What if you try to include the navigation property into the select: 如果您尝试将Navigation属性包含在select中,该怎么办:
IQueryable<OrganizationViewModel> query = _context.Organizations
.Include(o => o.CommunicationViewModels)
...
I figured it out! 我想到了!
This is my new query: 这是我的新查询:
DateTime sixMonthsAgo = DateTime.Today.AddMonths(-6);
int pageIndex = 1; // Would be passed in
int pageSize = 3;
IQueryable<OrganizationViewModel> query = _context.Organizations
.AsNoTracking()
.Select(organization => new OrganizationViewModel
{
CommunicationViewModels = organization.Communications.Select(communication =>
new CommunicationViewModel
{
Id = communication.Id,
Date = communication.Date
})
.OrderByDescending(communicationViewModel => communicationViewModel.Date)
.Take(1)
.Where(communicationViewModel => communicationViewModel.Date <= sixMonthsAgo)
.AsQueryable()
})
.Where(organizationViewModel =>
(!searchViewModel.LimitToLastSixMonths || organizationViewModel.CommunicationViewModels.Any()));
int totalAmount = await query.CountAsync();
List<OrganizationViewModel> items = await query
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
Which now produces these two queries when searchViewModel.LimitToLastSizeMonths
is true
: 现在,当
searchViewModel.LimitToLastSizeMonths
为true
时, searchViewModel.LimitToLastSizeMonths
生成这两个查询:
SELECT COUNT(*)
FROM [Organizations] AS [organization]
WHERE EXISTS (
SELECT 1
FROM (
SELECT [t].[Id], [t].[Date]
FROM (
SELECT TOP(1) [communication].[Id], [communication].[Date]
FROM [Communications] AS [communication]
WHERE [organization].[Id] = [communication].[OrganizationId]
ORDER BY [communication].[Date] DESC
) AS [t]
WHERE [t].[Date] <= @__sixMonthsAgo_0
) AS [t0])
SELECT [organization].[Id]
FROM [Organizations] AS [organization]
WHERE EXISTS (
SELECT 1
FROM (
SELECT [t].[Id], [t].[Date]
FROM (
SELECT TOP(1) [communication].[Id], [communication].[Date]
FROM [Communications] AS [communication]
WHERE [organization].[Id] = [communication].[OrganizationId]
ORDER BY [communication].[Date] DESC
) AS [t]
WHERE [t].[Date] <= @__sixMonthsAgo_0
) AS [t0])
ORDER BY (SELECT 1)
OFFSET @__p_1 ROWS FETCH NEXT @__p_2 ROWS ONLY
Without the .AsQueryable()
it goes back to checking each record and performing the count and ski/take locally. 如果没有
.AsQueryable()
它将返回检查每个记录并在本地执行计数和滑雪/ .AsQueryable()
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.