SelectMany Expression Tree Expression.Call typeArguments

时间:2013-08-14 11:22:02

标签: expression-trees method-call linq

好吧,我在为SelectMany创建表达式树时遇到问题..特别是在typeArguments部分..

所以,我有一个包含如下表格的数据库:

[组](一对多)[GroupDetail](多对一)[项目](一对多)[ItemDetail]

  • GroupDetail.group是一个小组
  • GroupDetail.item是一个项目
  • ItemDetail.item是一个项目
  • Item.itemDetail是ItemDetail的集合
  • Group.groupDetail是GroupDetail的集合

因此您可以看到组详细信息只是Group和Item的多对多链接 和(一对多)是一对多的关系。

例如,数据如下:

Group, GroupDetail, Item, ItemDetail
------------------------------------
gr1, grDt1, ItemA, PartsAA
gr1, grDt1, ItemA, PartsAB
gr1, grDt2, ItemB, PartsBA
gr1, grDt2, ItemB, PartsBB

gr2, grDt3, ItemC, PartsCA
gr2, grDt4, ItemA, PartsAA
gr2, grDt4, ItemA, PartsAB

gr3, grDt4, ItemD, PartsDA
gr3, grDt5, ItemE, PartsEA

我想通过群组搜索选择项目及其每个细节 并将其作为某种视图类的集合返回..

类似下面这个函数:

public IQueryable<ItemGroupDetailView> getViewQ(IQueryable<GroupDetail> myQ)
{
    return myQ.SelectMany(
    m => m.item.itemDetail,
        (m, n) => new ItemGroupDetailView
        {
            groupName = m.group.name,
            groupDetailCount = m.group.groupDetail.Count,
            item = new ItemView
            {
                itemName = n.item.name,
                itemDetailCount = n.item.itemDetail.Count
            },
            itemDetail = new ItemDetailView
            {
                itemDetailName = n.name
            }        
        }
    );
}

就像上面那样但是我希望它是一个动态的exp树,所以也许我可以像以下一样使用它:

Filter filter = new Filter("gr1","ItemA"); // just a filter

var myQ = getSearchQ(filters); // it gets all the where etc, everything is fine here.. 
var viewQ = getViewQ(myQ);  // simply to convert the data to the view,.. where all the errors are
var finalQ = ApplyLimit(ApplyGrouping(ApplySorting(ApplySelect(myQ)));   // paging, sorting, grouping, etc.. 

// run the select.. get the count etc..

现在我想让它变得动态,但我似乎在SelectMany部分错了

这大致是我做SelectMany的事情:

第1步:我绑定属性/字段赋值..它来自某种list-string-configuration-kinda-thing,用于映射赋值

PropertyInfo pInfo; 
MemberExpression mExp;
// parse getproperty reflect etc... 
List<MemberAssignment> memberAssginments = new List<MemberAssignment>();
memberAssginments.Add(Expression.Bind(pInfo, mExp); 

第2步:然后是通常的成员init

MemberInitExpression mie =
    Expression.MemberInit(Expression.New
        (typeof(ItemGroupDetailView)), memberAssginments);

所以我明白了:

new ItemGroupDetailView
    {
        groupName = m.group.name,
        groupDetailCount = m.group.groupDetail.Count,
        item = new ItemView
        {
            itemName = n.item.name,
            itemDetailCount = n.item.itemDetail.Count
        },
        itemDetail = new ItemDetailView
        {
            itemDetailName = n.name
        }        
    }

步骤3:然后获取表达式collectionSelector&amp; resultSelector

ParamterExpression m = Expression.Parameter(typeof(GroupDetail),"m");
ParamterExpression n = Expression.Parameter(typeof(ItemDetail),"n");    

Expression<Func<GroupDetail, ItemDetail, ItemGroupDetailView>> exp2 =     
    Expression.Lambda<Func<GroupDetail, ItemDetail, ItemGroupDetailView>>
        (mie, new ParameterExpression[] { m, n });

我想我得到了我需要的东西,exp2(resultSelector):

    (m, n) => new ItemGroupDetailView
    {
        groupName = m.group.name,
        groupDetailCount = m.group.groupDetail.Count,
        item = new ItemView
        {
            itemName = n.item.name,
            itemDetailCount = n.item.itemDetail.Count
        },
        itemDetail = new ItemDetailView
        {
            itemDetailName = n.name
        }        
    }

以类似的方式我得到了另一个子句exp1(collectionSelector)

MemberExpression mEx = .... reflect get property/field etc..

Expression<Func<GroupDetail, IEnumerable<ItemDetail>>> exp1 = 
    Expression.Lambda<Func<GroupDetail, IEnumerable<ItemDetail>>>(mEx, m);

所以我明白了:

m => m.item.itemDetail

步骤4:然后获取selectMany MethodCallExpression本身

MethodCallExpression selectManyExp =
     Expression.Call(
        typeof(Queryable),
        "SelectMany",
        new Type[] { 
            typeof(GroupDetail), 
            typeof(Expression<Func<GroupDetail, IEnumerable<ItemDetail>>>),  
            typeof(Expression<Func<GroupDetail, ItemDetail, ItemGroupDetailView>>) 
        },
        new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
    );

根本不起作用..

(类型'System.Linq.Queryable'上没有泛型方法'SelectMany'与提供的类型参数和参数兼容。如果方法是非泛型的,则不应提供类型参数。)

所以我认为这里的主要问题是:

  1. 如何为此类selectMany查询构建表达式树
  2. 如何构建具有resultSelector&amp ;;的表达式查询? collectionSelector和多个参数..
  3. 以及为什么下面的代码有效,但Expression.Call总是错误..
  4. myQ.SelectMany(exp1, exp2);
    

    我想我不明白SelectMany或Expression Tree的工作原理.. :(

    但是,我需要它是动态的,因为属性/字段赋值绑定和源,选择器和结果是动态的

    public IQueryable<TView> getViewQ(IQueryable<T> myQ)
    {    
        // some code..
    }
    

    编辑1:

    切换exp1和exp2 ..现在exp1是collectionSelector,exp2是resultSelector ..

    编辑2:

    此外我尝试了几件事: 首先,我改变类型参数,就像Mike在下面所说的那样,但错误仍然相同

    MethodCallExpression selectManyExp =
         Expression.Call(
            typeof(Queryable),
            "SelectMany",
            new Type[] { 
                typeof(GroupDetail), 
                typeof(ItemDetail),  
                typeof(ItemGroupDetailView) 
            },
            new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
        );
    

    然后我尝试反思这个并检查

    System.Reflection.MethodInfo sminfo = null;
    System.Reflection.MethodInfo sminfo2 = null;
    
    IEnumerable<System.Reflection.MethodInfo> sminfos = typeof(Queryable)
        .GetMethods(System.Reflection.BindingFlags.Static
            | System.Reflection.BindingFlags.Public)
        .Where(xxx => xxx.Name.Equals("SelectMany"));
    
    foreach (System.Reflection.MethodInfo mi in sminfos)
    {
        if (mi.GetParameters().Count() == 3)
        {
            sminfo = mi;
        }
    }
    
    /*
    I ran this step by step to make sure that the method I get in sminfo is:
    public static IQueryable<TResult> SelectMany<TSource, TCollection, TResult>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, IEnumerable<TCollection>>> collectionSelector,
        Expression<Func<TSource, TCollection, TResult>> resultSelector
    );
    */
    
    sminfo2 = sminfo.MakeGenericMethod(
        new Type[] { 
            typeof(GroupDetail), typeof(ItemDetail), typeof(ItemGroupDetailView) 
        });
    
    MethodCallExpression selectManyExp = 
        Expression.Call(sminfo2, new Expression[] { exp1, exp2 });
    

    我得到了不同的错误: (为方法调用提供的参数数量不正确..)

    它告诉我该方法需要3个参数而不是2个,我想念的是IQueryable<GroupDetail>

    所以我回到Expression.Call并添加源参数

    MethodCallExpression selectManyExp =
         Expression.Call(
            typeof(Queryable),
            "SelectMany",
            new Type[] { 
                typeof(GroupDetail), 
                typeof(ItemDetail),  
                typeof(ItemGroupDetailView) 
            },
            new Expression[] { myQ.Expression, exp1, exp2 }
        );
    
    return (IQueryable<ItemGroupDetailView>)myQ.Provider.CreateQuery(selectManyExp);
    

    它有效..:D

    对于凌乱而长篇的帖子感到抱歉,我的英语不好...... :(

1 个答案:

答案 0 :(得分:0)

看起来你已经将类型参数与形式参数混为一谈。我相信您的类型参数应如下所示:

MethodCallExpression selectManyExp =
    Expression.Call(
        typeof(Queryable),
        "SelectMany",
        new Type[] { 
            typeof(GroupDetail), 
            typeof(ItemDetail),  
            typeof(ItemGroupDetailView) 
        },
        new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);