动态地将GroupBy添加到Lambda表达式

时间:2010-10-07 22:05:19

标签: linq expression-trees

好的,我承认我还没有完全“获取”lambda表达式和LINQ表达式树;我正在做的很多事情就是切割和粘贴,看看哪些有效。我看了很多文档,但我还没有找到我的“啊哈”时刻。

据说......

我正在尝试动态地将GroupBy表达式添加到我的Linq表达式中。我跟着这个问题: Need help creating Linq.Expression to Enumerable.GroupBy

并尝试实施我在那里看到的内容。

首先,我有我的数据库的实体类,以及一个名为ObjCurLocViewNormalized的表

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

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表达式基础和构建动态表达式树之间差距的文档,那就太棒了。我的知识显然有很大漏洞,但我认为这些信息可能对其他人有用。

感谢大家的时间,当然如果我在其他地方找到答案,我会在这里发布。

再次感谢。

2 个答案:

答案 0 :(得分:2)

解决方案应该非常简单:

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");

答案 1 :(得分:0)

要使其发挥作用,您需要做的就是:

    static public IQueryable<IGrouping<TValue, TResult>> addGroupBy<TValue, TResult>(
                this IQueryable<TResult> query, string columnName)
            {
            var providerType = query.Provider.GetType();

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

            ParameterExpression data = Expression.Parameter(iqueryableT, "query");
            ParameterExpression arg = Expression.Parameter(tableType, tableName);
            MemberExpression nameProperty = Expression.PropertyOrField(arg, columnName);
            Expression<Func<TResult, TValue>> lambda = Expression.Lambda<Func<TResult, TValue>>(nameProperty, arg);
            //here you already have delegate in the form of "TResult => TResult.columnName"
            return query.GroupBy(lambda);

        /*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;*/
    }

你会用以下方式称呼你:

var grouped = locations.addGroupBy<string, ObjCurLocViewNormalized>(childLocationFieldName);

第一个通用参数“string”我们用来说明你分组的元素类型。例如,您可以按“int”字段进行分组,方法调用将如下所示:

var grouped = locations.addGroupBy<int, ObjCurLocViewNormalized>(someFieldNameWithTheTypeOfInt);

修改 只是为了完成这个解决方案你的方式

 //return query.GroupBy(lambda);
    MethodCallExpression expression = Expression.Call(typeof (Enumerable),
                                                      "GroupBy",
                                                      new[] { typeof(TResult), typeof(TValue) },
                                                      data,
                                                      lambda);

    var result = Expression.Lambda(expression, data).Compile().DynamicInvoke(query);
    return ((IEnumerable<IGrouping<TValue, TResult>>)result).AsQueryable();