Ответ 1
Если вы явно хотите использовать библиотеку динамических запросов LINQ, мой ответ не будет таким, какой вы хотите, но если вы хотите, чтобы ваше поведение было желательным, и вы счастливы использовать обычный LINQ, то я думаю, что могу помочь.
По существу, я создал класс EntryGrouper
, который обрабатывает логику группировки по выбранным значениям в раскрывающихся списках, и я предположил, что переменные section
, page
и module
сохраняют эти значения. Я также предположил, что ObjectContext.OmniturePageModules
является перечислимым типа Entry
.
Итак, теперь ваш запрос LINQ становится следующим:
var entries = (from entry in ObjectContext.OmniturePageModules
where entry.StartOfWeek >= startDate
&& entry.StartOfWeek <= endDate
&& (section == "Total" || section == "All" || entry.Section == section)
&& (page == "Total" || page == "All" || entry.Page == page)
&& (module == "Total" || module == "All" || entry.Module == module)
select entry).ToArray(); // Force query execution
var grouping = from entry in entries
let grouper = new EntryGrouper(entry, section, page, module)
group entry by grouper into entryGroup
select new
{
SeriesName = entryGroup.Key.SeriesName,
Week = entryGroup.Key.StartOfWeek,
Clicks = entryGroup.Sum(p => p.Clicks),
};
Первый запрос используется для принудительного простого запроса запроса в базе данных и возврата только записей, которые вы хотите сгруппировать. Обычно запросы group by
вызывают базу данных несколько раз, поэтому запрос таким образом обычно выполняется намного быстрее.
Второй запрос группирует результаты первого запроса, создавая экземпляры класса EntryGrouper
в качестве ключа группировки.
Я включил свойство SeriesName
в класс EntryGrouper
, чтобы вся логика группировки была четко определена в одном месте.
Теперь класс EntryGrouper
довольно велик, так как для обеспечения возможности группировки он должен иметь свойства для StartOfWeek
, section
, page
и module
и содержать перегрузки Equals
и GetHashCode
и реализовать интерфейс IEquatable<Entry>
.
Вот он:
public class EntryGrouper : IEquatable<Entry>
{
private Entry _entry;
private string _section;
private string _page;
private string _module;
public EntryGrouper(Entry entry, string section, string page, string module)
{
_entry = entry;
_section = section;
_page = page;
_module = module;
}
public string SeriesName
{
get
{
return String.Format("{0}:{1}:{2}", this.Section, this.Page, this.Module);
}
}
public DateTime StartOfWeek
{
get
{
return _entry.StartOfWeek;
}
}
public string Section
{
get
{
if (_section == "Total" || _section == "All")
return _section;
return _entry.Section;
}
}
public string Page
{
get
{
if (_page == "Total" || _page == "All")
return _page;
return _entry.Page;
}
}
public string Module
{
get
{
if (_module == "Total" || _module == "All")
return _module;
return _entry.Module;
}
}
public override bool Equals(object other)
{
if (other is Entry)
return this.Equals((Entry)other);
return false;
}
public bool Equals(Entry other)
{
if (other == null)
return false;
if (!EqualityComparer<DateTime>.Default.Equals(this.StartOfWeek, other.StartOfWeek))
return false;
if (!EqualityComparer<string>.Default.Equals(this.Section, other.Section))
return false;
if (!EqualityComparer<string>.Default.Equals(this.Page, other.Page))
return false;
if (!EqualityComparer<string>.Default.Equals(this.Module, other.Module))
return false;
return true;
}
public override int GetHashCode()
{
var hash = 0;
hash ^= EqualityComparer<DateTime>.Default.GetHashCode(this.StartOfWeek);
hash ^= EqualityComparer<string>.Default.GetHashCode(this.Section);
hash ^= EqualityComparer<string>.Default.GetHashCode(this.Page);
hash ^= EqualityComparer<string>.Default.GetHashCode(this.Module);
return hash;
}
public override string ToString()
{
var template = "{{ StartOfWeek = {0}, Section = {1}, Page = {2}, Module = {3} }}";
return String.Format(template, this.StartOfWeek, this.Section, this.Page, this.Module);
}
}
Логика группировки этого класса выглядит просто так:
if (_page == "Total" || _page == "All")
return _page;
return _entry.Page;
Если я неправильно понял, как вы выпадающих значений включаете и выключаете группировку, вам просто нужно будет изменить эти методы, но суть этого кода в том, что при группировке он должен возвращать значение группы на основе значения в запись, и в противном случае она должна возвращать общее значение для всех записей. Если значение является общим для всех записей, то оно логически создает только одну группу, которая не совпадает с группировкой.
Если у вас больше выпадающих списков, которые вы группируете, вам нужно добавить дополнительные свойства в класс EntryGrouper
. Не забудьте добавить эти новые свойства в методы Equals
и GetHashCode
.
Таким образом, эта логика представляет собой динамическую группировку, которую вы хотели. Пожалуйста, дайте мне знать, помогли ли мне или вам нужно больше деталей.
Наслаждайтесь!