动态将GroupBy添加到Lambda表达式 [英] Dynamically Adding a GroupBy to a Lambda Expression

查看:370
本文介绍了动态将GroupBy添加到Lambda表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,我承认我还没有完全得到" lambda表达式和LINQ表达式树.我正在做的很多事情都是剪切和粘贴,然后看看有什么用.我查看了很多文档,但是仍然找不到我的"aha"时刻.

话虽如此...

我试图将GroupBy表达式动态添加到Linq表达式中.我在这里关注了这个问题: 需要帮助将Linq.Expression创建为Enumerable.GroupBy

并尝试实现我在那看到的东西.

首先,我有数据库的实体类,还有一个名为ObjCurLocViewNormalNormalized的表

我有一个可以进行初始调用的方法

public IQueryable<ObjCurLocViewNormalized> getLocations()
{
    IQueryable<ObjCurLocViewNormalized> res = (from loc in tms.ObjCurLocViewNormalized
                               select loc);
    return res;
}

所以我可以打电话给

IQueryable<MetAmericanLinqDataModel.ObjCurLocViewNormalized> locations = american.getLocations();

到目前为止没有问题.

现在,我想按任意列分组,调用如下:

var grouped = locations.addGroupBy(childLocationFieldName);

现在,我有一个方法:

static public System.Linq.IQueryable<System.Linq.IGrouping<string, TResult>> addGroupBy<TResult>(this IQueryable<TResult> query, string columnName)
{

    var providerType = query.Provider.GetType();
    // Find the specific type parameter (the T in IQueryable<T>)
    var iqueryableT = providerType.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
    var tableType = iqueryableT.GetGenericArguments()[0];
    var tableName = tableType.Name;

    var data = Expression.Parameter(iqueryableT, "query");
    var arg = Expression.Parameter(tableType, tableName);
    var nameProperty = Expression.PropertyOrField(arg, columnName);
    var lambda = Expression.Lambda<Func<TResult, string>>(nameProperty, arg);

    var expression = Expression.Call(typeof(Enumerable), 
                                    "GroupBy", 
                                    new Type[] { tableType, typeof(string) },
                                    data, 
                                    lambda);
    var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg); // this is the line that produces the error I describe below
    var result = query.GroupBy(predicate).AsQueryable();
    return result;
}

所有这些都可以编译,但是当我运行它时,出现错误:

System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable`1[System.Linq.IGrouping`2[System.String,MetAmericanLinqDataModel.ObjCurLocViewNormalized]]' cannot be used for return type 'System.String'

,错误来自此行:

 var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg);

我正在复制并改编这段代码,以使我成功地在表达式中动态添加Where子句中所做的工作.所以我有点在暗中刺伤.

如果有任何人可以帮助您对此有所了解,显然发布完整的工作代码并为我全心全意是不错的:),但是如果您可以列出错误原因或解决方法,围绕这些概念,这真是太好了.如果您可以找到可以真正帮助弥补Lambda表达式基础知识与构建动态表达式树之间的鸿沟的文档,那将是非常不错的.我的知识上显然有很多漏洞,但是我认为这些信息可能对其他人有用.

感谢大家的宝贵时间,当然,如果我在其他地方找到答案,我会在此处发布.

再次感谢.

解决方案

解决方案应该非常简单:

public static IQueryable<IGrouping<TColumn, T>> DynamicGroupBy<T, TColumn>(
    IQueryable<T> source, string column)
{
    PropertyInfo columnProperty = typeof(T).GetProperty(column);
    var sourceParm = Expression.Parameter(typeof(T), "x");
    var propertyReference = Expression.Property(sourceParm, columnProperty);
    var groupBySelector = Expression.Lambda<Func<T, TColumn>>(propertyReference, sourceParm);

    return source.GroupBy(groupBySelector);
}

假设这样的示例类:

public class TestClass
{
    public string TestProperty { get; set; }
}

您可以这样调用它:

var list = new List<TestClass>();
var queryable = list.AsQueryable();
DynamicGroupBy<TestClass, string>(queryable, "TestProperty");

Ok, I'll admit that I don't entirely "get" lambda expressions and LINQ expression trees yet; a lot of what I'm doing is cutting and pasting and seeing what works. I've looked over lots of documentation, but I still haven't found the my "aha" moment yet.

With that being said...

I'm attempting to dynamically add a GroupBy expression to my Linq expression. I followed the question here: Need help creating Linq.Expression to Enumerable.GroupBy

and tried to implement what I saw there.

First off, I've got entity classes for my database, and a table calledObjCurLocViewNormalized

I've got an method that does the initial call,

public IQueryable<ObjCurLocViewNormalized> getLocations()
{
    IQueryable<ObjCurLocViewNormalized> res = (from loc in tms.ObjCurLocViewNormalized
                               select loc);
    return res;
}

so I can call:

IQueryable<MetAmericanLinqDataModel.ObjCurLocViewNormalized> locations = american.getLocations();

No problem so far.

Now, I want to group by an arbitrary column, with a call like this:

var grouped = locations.addGroupBy(childLocationFieldName);

Right now, I have a method :

static public System.Linq.IQueryable<System.Linq.IGrouping<string, TResult>> addGroupBy<TResult>(this IQueryable<TResult> query, string columnName)
{

    var providerType = query.Provider.GetType();
    // Find the specific type parameter (the T in IQueryable<T>)
    var iqueryableT = providerType.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
    var tableType = iqueryableT.GetGenericArguments()[0];
    var tableName = tableType.Name;

    var data = Expression.Parameter(iqueryableT, "query");
    var arg = Expression.Parameter(tableType, tableName);
    var nameProperty = Expression.PropertyOrField(arg, columnName);
    var lambda = Expression.Lambda<Func<TResult, string>>(nameProperty, arg);

    var expression = Expression.Call(typeof(Enumerable), 
                                    "GroupBy", 
                                    new Type[] { tableType, typeof(string) },
                                    data, 
                                    lambda);
    var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg); // this is the line that produces the error I describe below
    var result = query.GroupBy(predicate).AsQueryable();
    return result;
}

All this compiles ok, but when I run it, I get the error:

System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable`1[System.Linq.IGrouping`2[System.String,MetAmericanLinqDataModel.ObjCurLocViewNormalized]]' cannot be used for return type 'System.String'

and the error comes from this line:

 var predicate = Expression.Lambda<Func<TResult, String>>(expression, arg);

I'm copying and adapting this code from successful work I did in dynamically added Where clauses to an expression. So I'm sort of stabbing in the dark here.

If anyone out there can help to shed some light on this, Obviously posting complete working code and doing all my thinking for me would be great :), but if you could just lay out just why this is wrong, or how to wrap my head around these concepts, that would be great. If you can point to documentation that can really help be bridge the gap between the basics of lambda expressions, and building dynamic expression trees, that would be great. There's obviously big holes in my knowledge, but I think this information could be useful to others.

thanks everyone for your time, and of course if I find the answer elsewhere, I'll post it here.

Thanks again.

Don

解决方案

The solution should be pretty simple:

public static IQueryable<IGrouping<TColumn, T>> DynamicGroupBy<T, TColumn>(
    IQueryable<T> source, string column)
{
    PropertyInfo columnProperty = typeof(T).GetProperty(column);
    var sourceParm = Expression.Parameter(typeof(T), "x");
    var propertyReference = Expression.Property(sourceParm, columnProperty);
    var groupBySelector = Expression.Lambda<Func<T, TColumn>>(propertyReference, sourceParm);

    return source.GroupBy(groupBySelector);
}

Assuming a sample class like this:

public class TestClass
{
    public string TestProperty { get; set; }
}

You invoke it like this:

var list = new List<TestClass>();
var queryable = list.AsQueryable();
DynamicGroupBy<TestClass, string>(queryable, "TestProperty");

这篇关于动态将GroupBy添加到Lambda表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆