动态 LINQ GroupBy 多列
我需要将以下 LINQ 查询转换为动态 LINQ,它接受基于用户输入的多个分组列。基本上我有一堆应用分组的下拉列表,我不想枚举分组的每种组合。如果动态 LINQ 失败,我可能必须手动构建 SQL 查询,但没有人愿意这样做。
var grouping = ( 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 )
group entry by new
{
entry.Page, // I want to be able to tell this anonymous type
entry.Module, // which columns to group by
entry.StartOfWeek // at runtime
}
into entryGroup
select new
{
SeriesName = section + ":" + entryGroup.Key.Page + ":" + entryGroup.Key.Module,
Week = entryGroup.Key.StartOfWeek,
Clicks = entryGroup.Sum( p => p.Clicks )
} );
我不知道如何做到这一点,因为动态 LINQ 在“hello world!”之外完全没有文档记录。选择/哪里/按情况排序。我只是无法弄清楚语法。
类似于:(?)
var grouping = ObjectContext.OmniturePageModules.Where(entry => 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 ))
.GroupBy("new (StartOfWeek,Page,Module)", "it")
.Select("new (Sum(Clicks) as Clicks, SeriesName = section + key.Page + Key.Module, Week = it.Key.StartOfWeek)");
我在 System.Linq.Dynamic 中使用 DynamicQueryable 类。请参阅:http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
后续: Enigmativity 的解决方案基本上有效。由于某种原因,它不想按日期时间“StartOfWeek”列进行分组——解决方法只是进行二次分组:
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
{
entryGroup.Key.SeriesName,
entryGroup.Key.Date,
Clicks = entryGroup.Sum( p => p.Clicks ),
};
var grouping2 = (from groups in grouping
group groups by new {groups.SeriesName, groups.Date } into entryGroup
select new
{
entryGroup.Key.SeriesName,
entryGroup.Key.Date,
Clicks = entryGroup.Sum( p => p.Clicks ),
} );
但这似乎会严重降低性能...=/
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是在 Dynamic LINQ 中——当然,您在运行时构建 GroupBy 和 Select 字符串:
这是正常的 LINQ 方式,直到一位同事指出为止——这基本上是 Enigmativity 的解决方案,没有使用 grouper 类:
Here it is in Dynamic LINQ -- of course you build the GroupBy and Select strings at runtime:
And here is the normal LINQ way that escaped me until a coworker pointed it out -- this is basically Enigmativity's solution without the grouper class:
如果您明确想要使用 LINQ 动态查询库,那么我的答案不会是您想要的,但如果您想要您想要的行为并且您很乐意使用常规 LINQ,那么我想我可以提供帮助。
本质上,我创建了一个
EntryGrouper
类,用于处理按下拉列表中所选值进行分组的逻辑,并且我假设变量section
,page
&module
保存这些值。我还假设ObjectContext.OmniturePageModules
是Entry
类型的可枚举。因此,您的 LINQ 查询现在变为以下两个:
第一个查询用于在数据库上强制执行简单的选择查询,并仅返回要分组的记录。一般来说,
group by
查询会多次调用数据库,因此以这种方式查询通常会快得多。第二个查询通过创建
EntryGrouper
类的实例作为分组键来对第一个查询的结果进行分组。我在
EntryGrouper
类中包含了一个SeriesName
属性,以便所有分组逻辑都整齐地定义在一个位置。现在,
EntryGrouper
类相当大,为了允许分组工作,它需要具有StartOfWeek
、Section
、属性页面
&Module
,并包含Equals
& 的重载。GetHashCode
方法,并实现IEquatable
接口。就是这样:
这个类的分组逻辑看起来就像这样:
如果我误解了下拉值如何打开和关闭分组,那么您应该只需要更改这些方法,但此代码的关键在于,当分组是它应该根据条目中的值返回一个组值,否则它应该返回所有条目的公共值。如果该值对于所有条目都是通用的,那么它在逻辑上仅创建一个组,这与根本不分组相同。
如果您有更多要分组的下拉菜单,则需要向
EntryGrouper
类添加更多属性。不要忘记将这些新属性添加到Equals
& 中。还有GetHashCode
方法。因此,这个逻辑代表了您想要的动态分组。如果我有帮助或者您需要更多详细信息,请告诉我。
享受!
If you explicitly want to use the LINQ Dynamic Query Library then my answer isn't going to be what you want, but if you want your desired behaviour and you're happy to use regular LINQ then I think I can help.
Essentially I've created an
EntryGrouper
class that handles the logic of grouping by the selected values in the dropdown lists and I've assumed that the variablessection
,page
&module
hold those values. I've also assumed thatObjectContext.OmniturePageModules
is an enumerable of typeEntry
.So your LINQ query now becomes these two:
The first query is used to force a simple select query on the database and return only the records that you want to group. Generally
group by
queries call the database multiple times so querying in this way is usually much faster.The second query groups the results of the first query by creating instances of the
EntryGrouper
class as the grouping key.I've included a
SeriesName
property in theEntryGrouper
class so that all of the grouping logic is neatly defined in one place.Now, the
EntryGrouper
class is quite large as, to allow grouping to work, it needs to have properties forStartOfWeek
,Section
,Page
&Module
, and contain overloads of theEquals
&GetHashCode
methods, and implement theIEquatable<Entry>
interface.Here it is:
The grouping logic of this class looks simply like this:
If I have misunderstood how you the dropdown values turn grouping on and off then you should just need to change these methods, but the crux of this code is that when grouping is on it should return a group value based on the value in the entry and otherwise it should return a common value for all entries. If the value is common for all entries then it logically only creates a single group which is the same as not grouping at all.
If you have more dropdowns that you're grouping by then you need to add more properties to the
EntryGrouper
class. Don't forget to add these new properties to theEquals
&GetHashCode
methods too.This logic, therefore, represents the dynamic grouping that you wanted. Please let me know if I've helped or if you need more detail.
Enjoy!
我知道这个问题发布已经有一段时间了,但我最近不得不处理类似的问题(按用户在运行时选择的多个列进行动态分组),所以这是我的看法。
用于创建分组 lambda 的辅助函数
用于执行内存中分组的函数。返回组。
如何使用:
关于性能下降,我认为这实际上不是一个(大)问题。即使动态构建分组 SQL,该查询也必须返回与没有分组的查询相同的行数。因此,尽管在此方法中分组不是由数据库完成的,但强制查询执行返回的行数与具有分组条件的假设 SQL 查询的行数相同。当然,数据库的性能可能会优于 C# 代码完成的内存中分组,但流量仅取决于必须分组的行(
条目
)数量。I know it's been a while since this question was posted but I had to deal with a similar problem recently (dynamic grouping by multiple columns selected by user in runtime) so here's my take on it.
Helper function for creating grouping lambdas
Function for doing the in-memory grouping. Returns groups.
How would it be used:
Regarding degrading performances, I don't think that's actually a (big) problem. Even if you constructed a grouping SQL dynamically, the query would have to return the same number of rows as a query without the grouping. So although in this approach the grouping is not done by the database, the number of rows returned by forced query execution is the same as it would be for the hypothetical SQL query with grouping criteria. Sure, database would probably outperform in-memory grouping done by the C# code but the amount of traffic depends solely on how many rows (
entries
) have to be grouped.