2

好吧,我在为 SelectMany 创建表达式树时遇到了问题。尤其是在 typeArguments 部分。

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

[Group](一对多)[GroupDetail](多对一)[Item](一对多)[ItemDetail]

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

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

例如,数据如下:

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步:我绑定属性/字段分配..它来自某种映射分配的列表字符串配置类型

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

第2步:然后是通常的成员初始化

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 & 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 和 collectionSelector 以及多个参数的表达式查询..
  3. 以及为什么下面的代码有效,但 Expression.Call 总是出错..
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 所说的那样更改了 type 参数,但错误仍然相同

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

很抱歉这篇凌乱而冗长的帖子,..我的英语不好.. :(

4

1 回答 1

0

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

MethodCallExpression selectManyExp =
    Expression.Call(
        typeof(Queryable),
        "SelectMany",
        new Type[] { 
            typeof(GroupDetail), 
            typeof(ItemDetail),  
            typeof(ItemGroupDetailView) 
        },
        new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);
于 2013-08-14T16:45:31.993 回答