如何重构linq查询以重用其部分

时间:2013-06-05 16:29:31

标签: 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))
                     .OrderByDescending(q => q.RedesignedAt)
                     .Take(1).Select(q => q.Type.DefinitionId).Contains(id)
                                   );

这是问题。每个信息亭都有一个历史名称集合,在应用程序的某些部分我们希望根据其最新状态(通过检查其类型或活动或其他一些数据)做一些事情,所以这部分查询将重复:

db.KioskDesignations.Where(q =>
          q.Kiosk.KioskId == kiosk.KioskId &&
          (!q.RedesignedAt.HasValue || q.RedesignedAt.Value <= DateTime.Now))
               .OrderByDescending(q => q.RedesignedAt).Take(1)

到目前为止,我已经尝试将此部分编写为函数,Func和Expression,并且它们都没有工作。请您告诉我如何重构此查询以便我可以重用重复部分? 非常感谢。

2 个答案:

答案 0 :(得分:1)

你可以链接Wheres例如:

    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));
    }

答案 1 :(得分:1)

我终于找到了解决办法:) 因为linq的where方法接受Func<T,bool>,所以我编写了一个返回Func<T,bool>的函数,并在我喜欢的地方调用它:

db.Kiosks
    .Where(QueryCurrentKioskDesignation(db, d => d.Type.DefinitionId == id))

此函数获取一个Func作为谓词,根据其当前的Designation数据过滤我们的kiosk。 这是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);
        }

<强>更新 我注意到只要我们使用IEnumerable<T>,Linq方法就返回Func<>(这意味着Linq立即调用函数并返回一个IEnumerable)但是最好使用表达式让Linq构建一个表达式树我们以后可以执行它。 为了实现这一点,我刚刚更改了QueryCurrentKioskDesignation签名以接受表达式并返回一个:

public static Expression<Func<Kiosk, bool>> QueryCurrentKioskDesignation(DataContext db,
                                                                     Expression<Func<KioskDesignation, bool>> predicate)

现在我可以使用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 */))

这就是我爱Linq的原因:)。