SelectMany Expression Tree Expression.Call typeArguments

SelectMany Expression Tree Expression.Call typeArguments

嗯,我在为selectmany创建表达式树时遇到问题。尤其是在typearguments部分。

所以,我有一个数据库,有如下表:

[组](一对多)[组详细信息](多对一)[项目](一对多)[项目详细信息]

  • groupdetail.group是一个组
  • groupdetail.item是一个项目
  • itemdetail.item是一个项
  • item.itemdetail是itemdetail的集合
  • group.groupdetail是groupdetail的集合

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

例如,数据如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

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

类似于下面的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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树,所以也许我可以这样使用它:

1
2
3
4
5
6
7
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部分我似乎弄错了

这大致就是我如何执行选择的许多操作:

步骤1:我绑定属性/字段分配。它来自某种列表字符串配置,类似于映射分配的东西

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

步骤2:然后是常规成员初始化

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

所以我得到这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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&ResultSelector

1
2
3
4
5
6
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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    (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)

1
2
3
4
MemberExpression mEx = .... reflect get property/field etc..

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

所以我得到这个:

1
m => m.item.itemDetail

步骤4:然后获取selectmany方法callexpression本身

1
2
3
4
5
6
7
8
9
10
11
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"。如果方法不是泛型的,则不应提供类型参数。)

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

  • 如何为此类selectmany查询构建表达式树
  • 如何构建具有resultsSelector&collectionSelector和多个参数的表达式查询。
  • 为什么下面的代码可以工作,但是表达式.call总是出错..
  • 1
    myQ.SelectMany(exp1, exp2);

    我想我不明白selectmany或expression树是如何工作的。:(

    但是,我需要这是动态的,因为属性/字段分配绑定和源、选择器和结果是动态的

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

    编辑1:

    切换exp1和exp2。现在exp1是collectionselector,exp2是resultsselector。

    编辑2:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    MethodCallExpression selectManyExp =
         Expression.Call(
            typeof(Queryable),
           "SelectMany",
            new Type[] {
                typeof(GroupDetail),
                typeof(ItemDetail),  
                typeof(ItemGroupDetailView)
            },
            new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
        );

    然后我试着思考一下这个和那个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    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

    所以我回到表达式,调用并添加源参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    MethodCallExpression selectManyExp =
        Expression.Call(
            typeof(Queryable),
           "SelectMany",
            new Type[] {
                typeof(GroupDetail),
                typeof(ItemDetail),  
                typeof(ItemGroupDetailView)
            },
            new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
    );