2016-12-22 9 views
3

Я пытаюсь вернуть некоторые данные отчета на основе частоты ввода пользователя (ежедневно, ежемесячно, ежегодно). Мой LINQ заключается в следующем:Группа Linq2SQL по свойству, известному во время выполнения

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)

+0

Как вы теперь наверняка, LINQ2SQL конвертирует запрос linq в SQL, и я думаю, что вы просто не можете использовать метод invoke. Вы можете просто вызвать вызов перед началом запроса и использовать результат в LINQ2SQL – OrcusZ

ответ

1

Мне удалось найти два способа сделать это, но не так мало, как 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([email protected]"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] 

 Смежные вопросы

  • Нет связанных вопросов^_^