获取相邻实体
Get Neighboring Entities
我正在编写一个函数,它根据日期从数据库中获取相邻(上一个和下一个)实体。我已经想出如何在 2 个查询中 return 邻居,但我更愿意同时拉出两个实体。
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);
}
我想在单个查询中提取上一个和下一个,最好是以一种不会因翻译过于复杂的表达式而生成 sql 庞然大物的方式。
编辑
我在想有一种方法可以做到这一点,如果我删除开始日期过滤器的位置并计算距离,而不是我仍然卡住了,但我觉得应该可以工作。
var previous = query
.Select(x => new {
Entity = x,
Distance = DbFunctions.DiffDays(x.StartDate, startDate)
})
.Where(x => x.Distance != 0);
注意:假定每个实体都有一个唯一的开始日期。
有没有简单的方法可以在单个查询中提取上一个和下一个实体?
把前后的去掉中间的怎么样?
我相信这仍会生成两个单独的 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();
这与在 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 core 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]
我正在编写一个函数,它根据日期从数据库中获取相邻(上一个和下一个)实体。我已经想出如何在 2 个查询中 return 邻居,但我更愿意同时拉出两个实体。
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);
}
我想在单个查询中提取上一个和下一个,最好是以一种不会因翻译过于复杂的表达式而生成 sql 庞然大物的方式。
编辑 我在想有一种方法可以做到这一点,如果我删除开始日期过滤器的位置并计算距离,而不是我仍然卡住了,但我觉得应该可以工作。
var previous = query
.Select(x => new {
Entity = x,
Distance = DbFunctions.DiffDays(x.StartDate, startDate)
})
.Where(x => x.Distance != 0);
注意:假定每个实体都有一个唯一的开始日期。
有没有简单的方法可以在单个查询中提取上一个和下一个实体?
把前后的去掉中间的怎么样?
我相信这仍会生成两个单独的 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();
这与在 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 core 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]