C# 如何重构linq查询以重用其部分
我尝试重构此查询已有一段时间,但没有任何运气:C# 如何重构linq查询以重用其部分,c#,linq,refactoring,reusability,C#,Linq,Refactoring,Reusability,我尝试重构此查询已有一段时间,但没有任何运气: db.Kiosks.Where(kiosk => db.KioskDesignations.Where( q => q.Kiosk.KioskId == kiosk.KioskId && (!q.RedesignedAt.HasValue||q.RedesignedAt.Value<= DateTime.Now))
db.Kiosks.Where(kiosk => db.KioskDesignations.Where(
q =>
q.Kiosk.KioskId == kiosk.KioskId &&
(!q.RedesignedAt.HasValue||q.RedesignedAt.Value<= DateTime.Now))
.OrderByDescending(q => q.RedesignedAt)
.Take(1).Select(q => q.Type.DefinitionId).Contains(id)
);
db.Kiosks.Where(kiosk=>db.KioskDesignations.Where(
q=>
q、 Kiosk.KioskId==Kiosk.KioskId&&
(!q.RedesignedAt.HasValue | | q.RedesignedAt.Value q.RedesignedAt)
.Take(1).选择(q=>q.Type.DefinitionId).包含(id)
);
问题是这样的。每个信息亭都有一个历史名称集合,在应用程序的某些部分,我们希望根据其最新状态(通过检查其类型、活动或其他数据)执行某些操作,因此这部分查询将重复:
db.KioskDesignations.Where(q =>
q.Kiosk.KioskId == kiosk.KioskId &&
(!q.RedesignedAt.HasValue || q.RedesignedAt.Value <= DateTime.Now))
.OrderByDescending(q => q.RedesignedAt).Take(1)
db.KioskDesignations.Where(q=>
q、 Kiosk.KioskId==Kiosk.KioskId&&
(!q.RedesignedAt.HasValue | | q.RedesignedAt.Value q.RedesignedAt)。取(1)
到目前为止,我已经尝试将此部分作为函数、Func和表达式编写,但它们都不起作用。请告诉我如何重构此查询,以便重用重复部分?
非常感谢。您可以链接位置,例如:
private void Something()
{
var query = GetStandardWhere(db.Kiosks);
query = query.Where( //some new criteria);
return query
.OrderByDescending(q => q.RedesignedAt)
.Take(1).Select(q => q.Type.DefinitionId).Contains(id)
);
}
private IQueryable<KioskDesignation> GetStandardWhere(IQueryable<KioskDesignation> query)
{
return
query.Where(
kiosk =>
db.KioskDesignations.Where(
q =>
q.Kiosk.KioskId == kiosk.KioskId &&
(!q.RedesignedAt.HasValue || q.RedesignedAt.Value <= DateTime.Now));
}
private void Something()
{
var query=GetStandardWhere(db.Kiosks);
query=query.Where(//一些新条件);
返回查询
.OrderByDescending(q=>q.RedesignedAt)
.Take(1).选择(q=>q.Type.DefinitionId).包含(id)
);
}
专用IQueryable GetStandardWhere(IQueryable查询)
{
返回
查询,在哪里(
信息亭=>
db.KioskDesignations.Where(
q=>
q、 Kiosk.KioskId==Kiosk.KioskId&&
(!q.RedesignedAt.HasValue | | q.RedesignedAt.Value我终于找到了解决方案:)
因为linq的Where方法接受Func
,所以我编写了一个函数,返回一个Func
,并在我的Where中调用它,如下所示:
db.Kiosks
.Where(QueryCurrentKioskDesignation(db, d => d.Type.DefinitionId == id))
这个函数获取一个Func作为谓词,根据当前的指定数据过滤我们的信息亭。
下面是QueryCurrentKioskDesignation函数:
public static Func<Kiosk, bool> QueryCurrentKioskDesignation(DataContext db,
Func<KioskDesignation, bool> predicate)
{
return k => db.KioskDesignations.Where(q => q.Kiosk.KioskId == k.KioskId &&
(!q.RedesignedAt.HasValue ||
q.RedesignedAt.Value <= DateTime.Now))
.OrderByDescending(q => q.RedesignedAt)
.Take(1).Any(predicate);
}
public static Func QueryCurrentKioskDesignation(DataContext db,
Func谓词)
{
返回k=>db.KioskDesignations.Where(q=>q.Kiosk.KioskId==k.KioskId&&
(!q.RedesignedAt.HasValue)||
q、 重新设计的AT.值q.重新设计的AT)
.取(1).任何(谓语);
}
更新:
我注意到,每当我们使用Func
时,Linq方法都会返回一个IEnumerable
(这意味着Linq会立即调用函数并返回一个IEnumerable),但最好使用表达式让Linq构建一个表达式树,我们可以在以后执行它。
为了实现这一点,我刚刚将QueryCurrentKioskDesignation签名更改为接受表达式并返回表达式:
public static Expression<Func<Kiosk, bool>> QueryCurrentKioskDesignation(DataContext db,
Expression<Func<KioskDesignation, bool>> predicate)
公共静态表达式QueryCurrentKioskDesignation(DataContext db,
表达式(谓词)
现在,我可以使用IQueryalbe,只需对数据库进行一次查询,就可以获得我想要的所有数据,并向您展示它的美妙之处。这是我使用EFProf生成的查询
SELECT TOP (20) [Extent1].[KioskId] AS [KioskId],
[Extent1].[Code] AS [Code],
[Extent1].[Barcode] AS [Barcode],
[Extent1].[Notes] AS [Notes],
[Extent1].[CheckedAt] AS [CheckedAt],
[Extent1].[SearchKeywords] AS [SearchKeywords],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[CreatedBy] AS [CreatedBy],
[Extent1].[LastEditAt] AS [LastEditAt],
[Extent1].[LastEditBy] AS [LastEditBy],
[Extent1].[Guild_KioskGuildId] AS [Guild_KioskGuildId]
FROM [dbo].[Kiosks] AS [Extent1]
WHERE (EXISTS (SELECT 1 AS [C1]
FROM (SELECT TOP (1) [Project1].[Activity_DefinitionId] AS [Activity_DefinitionId]
FROM (SELECT [Extent2].[RedesignedAt] AS [RedesignedAt],
[Extent2].[Activity_DefinitionId] AS [Activity_DefinitionId]
FROM [dbo].[KioskDesignations] AS [Extent2]
WHERE ([Extent2].[Kiosk_KioskId] = [Extent1].[KioskId])
AND (([Extent2].[RedesignedAt] IS NULL)
OR ([Extent2].[RedesignedAt] <= (SysDateTime())))) AS [Project1]
ORDER BY [Project1].[RedesignedAt] DESC) AS [Limit1]
WHERE (CASE
WHEN (0 /* @p__linq__0 */ = 1) THEN
CASE
WHEN (14 = [Limit1].[Activity_DefinitionId]) THEN cast(1 as bit)
WHEN (14 <> [Limit1].[Activity_DefinitionId]) THEN cast(0 as bit)
END
WHEN (14 <> [Limit1].[Activity_DefinitionId]) THEN cast(1 as bit)
WHEN (14 = [Limit1].[Activity_DefinitionId]) THEN cast(0 as bit)
END) = 1))
AND (EXISTS (SELECT 1 AS [C1]
FROM (SELECT TOP (1) [Project3].[Type_DefinitionId] AS [Type_DefinitionId]
FROM (SELECT [Extent3].[RedesignedAt] AS [RedesignedAt],
[Extent3].[Type_DefinitionId] AS [Type_DefinitionId]
FROM [dbo].[KioskDesignations] AS [Extent3]
WHERE ([Extent3].[Kiosk_KioskId] = [Extent1].[KioskId])
AND (([Extent3].[RedesignedAt] IS NULL)
OR ([Extent3].[RedesignedAt] <= (SysDateTime())))) AS [Project3]
ORDER BY [Project3].[RedesignedAt] DESC) AS [Limit2]
WHERE [Limit2].[Type_DefinitionId] = 4 /* @p__linq__1 */))
选择顶部(20)[Extent1].[KioskId]作为[KioskId],
[Extent1].[Code]作为[Code],
[Extent1]。[Barcode]作为[Barcode],
[extend1].[Notes]作为[Notes],
[Extent1]。[CheckedAt]作为[CheckedAt],
[Extent1]。[SearchKeywords]作为[SearchKeywords],
[Extent1]。[CreatedAt]作为[CreatedAt],
[Extent1]。[CreatedBy]作为[CreatedBy],
[Extent1][LastEditAt]作为[LastEditAt],
[Extent1][LastEditBy]作为[LastEditBy],
[Extent1]。[Guild\u KioskGuildId]作为[Guild\u KioskGuildId]
从[dbo].[Kiosks]作为[Extent1]
其中(存在)(选择1作为[C1]
从中(选择顶部(1)[Project1].[Activity\u DefinitionId]作为[Activity\u DefinitionId]
从中(选择[Extent2]。[RedesignedAt]作为[RedesignedAt],
[Extent2]。[Activity_DefinitionId]作为[Activity_DefinitionId]
来自[dbo]。[KioskDesignations]作为[Extent2]
其中([Extent2].[Kiosk_KioskId]=[Extent1].[KioskId])
和(([Extent2].[RedesignedAt]为空)
或([Extent2].[RedesignedAt]缩进…太可怕了…Where中的.Contains让我觉得你可以用任何扩展方法使它变得简单一点。你试过了吗?对不起,这是我能马上做的最好的了。@Garrisonely,是的,我也试过了,但没什么区别,我仍然无法提取逻辑并将其放进去其他地方。@Garrisonnely,谢谢编辑:)谢谢你的回复。这个解决方案的问题是我必须在两个不同的地方实现获取最新指定的逻辑(因为OrderBy和Take(1)是逻辑的一部分),你可以重构OrderBy并接受调用