【发布时间】:2013-08-16 07:48:40
【问题描述】:
好吧,我在为 SelectMany 创建表达式树时遇到了问题。尤其是在 typeArguments 部分。
所以,我有一个包含如下表格的数据库:
[Group](一对多)[GroupDetail](多对一)[Item](一对多)[ItemDetail]
- GroupDetail.group 是一个组
- GroupDetail.item 是一个项目
- ItemDetail.item 是一个项目
- Item.itemDetail 是 ItemDetail 的集合
- Group.groupDetail 是 GroupDetail 的集合
因此您可以看到组详细信息只是组和项目的多对多链接 and (one to many) 是一对多的关系..
例如,数据如下:
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
}
}
第三步:然后得到表达式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”与提供的类型参数和参数兼容。如果方法是非泛型的,则不应提供类型参数。)
所以我认为这里的主要问题是:
- 如何为此类 selectMany 查询构建表达式树
- 如何构建具有 resultSelector 和 collectionSelector 以及多个参数的表达式查询..
- 以及为什么下面的代码有效,但 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 所说的那样更改类型参数,但错误仍然相同
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
抱歉,这篇凌乱而冗长的帖子,..我的英语不好..:(
【问题讨论】:
-
这是一个问题......
-
如果你能把它浓缩成一个完整的功能代码块,那将是非常有帮助的......
标签: expression-trees method-call linq