我正在编写一个函数,该函数根据日期从数据库中获取相邻的(上一个和下一个)实体。我已经找到了如何在2个查询中返回邻居的方法,但是我希望一次可以同时拉两个实体。
public interface IHasDateRange
{
DateTime StartDate { get; set; }
DateTime EndDate { get; set; }
}
public static (TEntity Previous, TEntity Next) GetNeighborsOrDefault<TEntity>(
this IQueryable<TEntity> query, JustDate startDate)
where TEntity : class, IHasDateRange
{
var previous = query.Where(x => x.StartDate < startDate)
.OrderByDescending(x => x.StartDate)
.FirstOrDefault();
var next = query.Where(x => x.StartDate > startDate)
.OrderBy(x => x.StartDate)
.FirstOrDefault();
return (previous, next);
}
我想在单个查询中提取上一个和下一个,最好采用一种不会因翻译过于复杂的Expression而产生sql庞然大物的方式。
编辑 我在想如果我删除开始日期过滤器的位置并计算距离的话,有一种方法可以解决,但是我仍然卡住了,但是我觉得应该可以工作。
var previous = query
.Select(x => new {
Entity = x,
Distance = DbFunctions.DiffDays(x.StartDate, startDate)
})
.Where(x => x.Distance != 0);
注意:假定每个实体都有一个唯一的起始日期。
有没有一种简单的方法可以在单个查询中提取上一个和下一个实体?
答案 0 :(得分:1)
如何去除前后的并消除中间的东西?
我相信,这仍然会生成两个单独的SQL查询-一个用于获取Count()
,另一个用于获取结果,但是除非您想为EF添加ROW_NUMBER
支持(您可以扩展EF Core,为此),我认为没有更好的方法:
var previousAndNext = query.OrderBy(x => x.StartDate)
.Skip(query.Where(x => x.StartDate < startDate).Count()-1)
.Take(3)
.Where(x => x.StartDate != startDate)
.Take(2) // if startDate not in DB, just get previous and next
.ToList();
答案 1 :(得分:1)
这与从startDate
之前的第一个日期删除(包括)这两个实体相同。
query.Where(e => e.StartDate != startDate
&& e.StartDate >= query.OrderByDescending(e1 => e1.StartDate)
.Where(e1 => e1.StartDate < startDate).Select(e1 => e1.StartDate).FirstOrDefault())
.OrderBy(e => e.StartDate)
.Take(2)
如您所见,尽管第二个查询现在是一个主查询中的子查询,但您无法避免运行两个查询。
在EF6中,这会生成一个中等复杂的查询,如下所示:
SELECT TOP (2)
...
FROM ( SELECT
...
FROM [dbo].[Entity] AS [Extent1]
INNER JOIN (SELECT TOP (1) [Project1].[StartDate] AS [StartDate]
FROM ( SELECT
[Extent2].[StartDate] AS [StartDate]
FROM [dbo].[Entity] AS [Extent2]
WHERE [Extent2].[StartDate] < @p__linq__1
) AS [Project1]
ORDER BY [Project1].[StartDate] DESC ) AS [Limit1] ON 1 = 1
WHERE ( NOT (([Extent1].[StartDate] = @p__linq__0) AND ((CASE WHEN ([Extent1].[StartDate] IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END) = 0))) AND ([Extent1].[StartDate] >= [Limit1].[StartDate])
) AS [Project2]
ORDER BY [Project2].[StartDate] ASC
看到EF核心3.1.3生成这样的简单查询,我感到很惊讶:
SELECT TOP(@__p_2) ...
FROM [Entity] AS [e]
WHERE (([e].[StartDate] <> @__startDate_0) OR [e].[StartDate] IS NULL) AND ([e].[StartDate] >= (
SELECT TOP(1) [e0].[StartDate]
FROM [Entity] AS [e0]
WHERE [e0].[StartDate] < @__startDate_1
ORDER BY [e0].[StartDate] DESC))
ORDER BY [e].[StartDate]