我编写了以下查询,该查询选择分组数据键列并对Amount列求和。它很完美。
private static IEnumerable<GroupSum> GetListOfGroupedRows(IEnumerable<IGrouping<GroupKey, DataRow>> queryGroup)
{
IEnumerable<GroupSum> querySelect = queryGroup
.Select(g => new GroupSum
{
KeyS0 = g.Key.KeyS0,
KeyS1 = g.Key.KeyS1,
AggN0 = g.Sum(row => row.Field<double>("Amount"))
});
return querySelect;
}
该查询使用以下类型进行分组和求和。
private class GroupKey : IEquatable<GroupKey>
{
public string KeyS0 { get; set; }
public string KeyS1 { get; set; }
public bool Equals(GroupKey other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return string.Equals(this.KeyS0, other.KeyS0) &&
string.Equals(this.KeyS1, other.KeyS1);
}
public override int GetHashCode()
{
int hash0 = this.KeyS0 == null ? 0 : this.KeyS0.GetHashCode();
int hash1 = this.KeyS1 == null ? 0 : this.KeyS1.GetHashCode();
return hash0 + 31 * hash1;
}
}
private class GroupSum : GroupKey
{
public Double AggN0 { get; set; }
}
下一步,我想使用Linq表达式编写等效查询
我遇到了一个我不知道如何创建MethodCallExpression的问题:
g.Sum(row =&gt; row.Field(&#34; Amount&#34;))
我编写了以下代码。我在评论中标出了我被困的地方。
private static void GetListOfGroupedRowsExpress()
{
//The MethodInfo for generic Field<T>(DataRow, String) can be retrieved by:
MethodInfo methInfo = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) });
ParameterExpression expRow = Expression.Parameter(typeof(DataRow), "row"); //Parametr: (row =>....)
//Property to bind
PropertyInfo propertyInfo = typeof(GroupSum).GetProperty("AggN0");
//This returns properly: row.Field<double>("Amount")
MethodCallExpression expCall = GetFieldCallExpression(expRow, methInfo, propertyInfo.PropertyType, "Amount");
//This returns properly: row => row.Field<double>("Amount")
LambdaExpression expRowValues = Expression.Lambda<Func<DataRow, double>>(expCall, expRow);
NewExpression expNewGroupKey = Expression.New(typeof(GroupSum));
ParameterExpression expG = Expression.Parameter(typeof(GroupSum), "g");
//This returns properly method info for: double Sum<T>()
MethodInfo methodInfoSum = typeof(Queryable).GetMethods().First(m =>
m.Name == "Sum"
&& m.ReturnType == typeof(double)
&& m.IsGenericMethod
);
//This returns properly method info for: double Sum<DataRow>()
MethodInfo methodInfoSumDataRow = methodInfoSum.MakeGenericMethod(new Type[] { typeof(DataRow) });
//And here I'm stuck. The code below compiles but at runtime it throws an error:
//Expression of type 'TestLinq.TestLinqDataTable+GroupSum' cannot be used for parameter of type 'System.Linq.IQueryable`1[System.Data.DataRow]' of method 'Double Sum[DataRow](System.Linq.IQueryable`1[System.Data.DataRow], System.Linq.Expressions.Expression`1[System.Func`2[System.Data.DataRow,System.Double]])'
MethodCallExpression expSumRows = Expression.Call(
null,
methodInfoSumDataRow,
expG,
expRowValues);
}
private static MethodCallExpression GetFieldCallExpression(ParameterExpression expRow, MethodInfo methodFieldGeneric,
Type type, string columnName)
{
List<Expression> list = new List<Expression>();
list.Add(expRow);
ConstantExpression expColumnName = Expression.Constant(columnName, typeof(string));
list.Add(expColumnName);
MethodInfo methodFieldTyped = methodFieldGeneric.MakeGenericMethod(type);
MethodCallExpression expCall = Expression.Call(null, methodFieldTyped, list);
return expCall;
}
请问有人如何帮助我构建Sum()的调用表达式?
答案 0 :(得分:1)
我对您的代码进行了一些更改:
private static Func<IGrouping<GroupKey, DataRow>, double> GetFunc()
{
//row => row.Field<double>("Amount")
//The MethodInfo for generic Field<T>(DataRow, String) can be retrieved by:
MethodInfo methInfo = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) });
ParameterExpression expRow = Expression.Parameter(typeof(DataRow), "row"); //Parametr: (row =>....)
//Property to bind
PropertyInfo propertyInfo = typeof(GroupSum).GetProperty(nameof(GroupSum.AggN0));
//This returns properly: row.Field<double>("Amount")
MethodCallExpression expCall = GetFieldCallExpression(expRow, methInfo, propertyInfo.PropertyType, "Amount");
//This returns properly: row => row.Field<double>("Amount")
var expRowValues = Expression.Lambda(expCall, expRow);
ParameterExpression expQuerygroup = Expression.Parameter(typeof(IGrouping<GroupKey, DataRow>), "g");
MethodCallExpression expSumRows = Expression.Call(typeof(Enumerable), nameof(Enumerable.Sum), new[] { expRow.Type }, expQuerygroup, expRowValues);
var sum = Expression.Lambda<Func<IGrouping<GroupKey, DataRow>, double>>(expSumRows, expQuerygroup);
return sum.Compile();
}
private static MethodCallExpression GetFieldCallExpression(ParameterExpression expRow, MethodInfo methodFieldGeneric, Type type, string columnName)
{
ConstantExpression expColumnName = Expression.Constant(columnName, typeof(string));
MethodInfo methodFieldTyped = methodFieldGeneric.MakeGenericMethod(type);
MethodCallExpression expCall = Expression.Call(null, methodFieldTyped, expRow, expColumnName);
return expCall;
}
查找和处理泛型方法的Expression.Call
有一个很棒的重载,你不需要一个数组/ List<>
来调用Expression.Call
,因为它有{{1}过载。
请注意,我已将您的代码更改为params
...我不认为您可以使用Enumerable
执行所需操作...但您可以尝试将其更改回来。请注意,尽管您尝试在Queryable
类型(仅用于发现AggN0
类型的PropertyInfo propertyInfo
)上使代码“通用”,但{{{ 1}}关键字出现在难以删除的地方(AggN0
方法的返回类型)