簡體   English   中英

Linq2SQL組由運行時已知的屬性組成

[英]Linq2SQL group by a property that is known at runtime

我試圖根據用戶輸入頻率(每日,每月,每年)返回一些報告數據。 我的LINQ如下:

var dataQuery = DataAccess.AppEventRepository.AllNoTracking;

// get property func based on provided filter
Func<AppEvent, DateTime?> timeGroupProp = null;
if (filters.TimeSpanId == (int)TimeSpanEnum.Daily) timeGroupProp = e => e.InsertDay;
if (filters.TimeSpanId == (int) TimeSpanEnum.Monthly) timeGroupProp = e => e.InsertMonth;
if (filters.TimeSpanId == (int) TimeSpanEnum.Yearly) timeGroupProp = e => e.InsertYear;

 var groupedDataQuery = dataQuery
    // downgrading to LINQ2Object, because Invoke is not supported in LINQ2SQL
    .ToList()
    .GroupBy(e => new {InsertGroupProp = timeGroupProp?.Invoke(e), e.CountryId})
    .Select(grp => new AuditReportGroupingDataModel
    {
        GroupTime = grp.Key.InsertGroupProp.Value,
        CountryId = grp.Key.CountryId.Value,
        Count = grp.Count()
     });

這可以正常工作,但問題是在從SQL檢索所有數據后完成分組。 未來事件的數量可能會增加到數十萬,我預計性能會下降。

問題:是否可以編寫我的查詢以便在服務器端級別進行分組? (完全使用LINQ2SQL,不降級到LINQ2Object)

我設法找到了兩種方法,但不是像Invoke試驗那么小。

0)我用來存儲數據的一些POCO

public class AuditReportGroupingDataModelBase
{
    public DateTime GroupTime { get; set; }
    public int CountryId { get; set; }
}

public class AuditReportGroupingDataModel : AuditReportGroupingDataModelBase
{
    public int Count { get; set; }
}

1)丑陋的方式 - 在GroupBy中使用條件運算符

我的少數可能性允許三元運算符使用。 但是,對於增加的選項數量,這不能正常工作。

var groupedDataQuery = dataQuery
    .GroupBy(e => new AuditReportGroupingDataModelBase
    {
        GroupTime = (filters.TimeSpanId == (int)TimeSpanEnum.Daily ? e.InsertDay : filters.TimeSpanId == (int)TimeSpanEnum.Monthly ? e.InsertMonth : e.InsertDay).Value,
        CountryId = e.CountryId.Value
    })
    .Select(grp => new AuditReportGroupingDataModel
    {
        GroupTime = grp.Key.GroupTime,
        CountryId = grp.Key.CountryId,
        Count = grp.Count()
    });

這可行,但會產生一個丑陋且不那么有效的SQL語句:

exec sp_executesql N'SELECT 
    1 AS [C1], 
    [GroupBy1].[K2] AS [C2], 
    [GroupBy1].[K1] AS [CountryId], 
    [GroupBy1].[A1] AS [C3]
    FROM ( SELECT 
        [Filter1].[K1] AS [K1], 
        [Filter1].[K2] AS [K2], 
        COUNT([Filter1].[A1]) AS [A1]
        FROM ( SELECT 
            [Extent1].[CountryId] AS [K1], 
            CASE WHEN (1 = @p__linq__0) THEN [Extent1].[InsertDay] WHEN (2 = @p__linq__1) THEN [Extent1].[InsertMonth] ELSE [Extent1].[InsertDay] END AS [K2], 
            1 AS [A1]
            FROM [dbo].[AppEvent] AS [Extent1]
            WHERE ([Extent1].[EventTypeId] IN (1)) AND ([Extent1].[CountryId] IS NOT NULL)
        )  AS [Filter1]
        GROUP BY [K1], [K2]
    )  AS [GroupBy1]',N'@p__linq__0 int,@p__linq__1 int',@p__linq__0=1,@p__linq__1=1

2)更好的方法 - 基於值的GroupBy表達式

        IQueryable<IGrouping<AuditReportGroupingDataModelBase, AppEvent>> groupedDataQueryInterm = null;
        if (filters.TimeSpanId == (int)TimeSpanEnum.Daily) groupedDataQueryInterm = dataQuery.GroupBy(e => new AuditReportGroupingDataModelBase { GroupTime = e.InsertDay.Value, CountryId = e.CountryId.Value });
        if (filters.TimeSpanId == (int)TimeSpanEnum.Monthly) groupedDataQueryInterm = dataQuery.GroupBy(e => new AuditReportGroupingDataModelBase { GroupTime = e.InsertMonth.Value, CountryId = e.CountryId.Value });
        if (filters.TimeSpanId == (int)TimeSpanEnum.Yearly) groupedDataQueryInterm = dataQuery.GroupBy(e => new AuditReportGroupingDataModelBase { GroupTime = e.InsertYear.Value, CountryId = e.CountryId.Value });
        if (groupedDataQueryInterm == null)
            throw new InvalidEnumArgumentException($@"Invalid value provided to {nameof(filters.TimeSpanId)}");

        var groupedDataQuery = groupedDataQueryInterm
            .Select(grp => new AuditReportGroupingDataModel
            {
                GroupTime = grp.Key.GroupTime,
                CountryId = grp.Key.CountryId,
                Count = grp.Count()
            })

這會生成更好的SQL:

SELECT 
    1 AS [C1], 
    [GroupBy1].[K2] AS [InsertDay], 
    [GroupBy1].[K1] AS [CountryId], 
    [GroupBy1].[A1] AS [C2]
    FROM ( SELECT 
        [Extent1].[CountryId] AS [K1], 
        [Extent1].[InsertDay] AS [K2], 
        COUNT(1) AS [A1]
        FROM [dbo].[AppEvent] AS [Extent1]
        WHERE ([Extent1].[EventTypeId] IN (1)) AND ([Extent1].[CountryId] IS NOT NULL)
        GROUP BY [Extent1].[CountryId], [Extent1].[InsertDay]
    )  AS [GroupBy1]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM