如果问题没有真正意义,我深表歉意,
我们在某些上下文中使用 C#、Entity Framework 和 Linq。
所以问题是:
用户可以选择要总计的多个字段,例如数据集中的总金额、 Netty 、增值税,然后在运行标准查询以返回数据时,查询必须对这些列进行总计以进行搜索。
我正在尝试将这些语句转换为动态表达式树。
List<string> propFrom = new List<string>()
{
"Class1.PropertyName",
"Class1.AnotherPropertyName"
}
Expression<Func<Grouped<Class1>, Class2>> select = (x => new Class2()
{
Class1Prop = x.Sum(s => s.Class2Prop),
Class1AntoherProp = x.Sum(s => s.Class2AnotherProp)
})
Class1 上的属性与 Class2 匹配,并且只有字符串列表中的属性才会被求和并在选择列表中选择。
我了解如何为标准 select 语句生成表达式树,而不是如何在 group by 和使用 sum 之后执行一个表达式树。
如果有人能思考如何重新表述问题或对答案有任何帮助,我们将不胜感激。
--更多细节--
应用程序显示发票周围的数据表
表格看起来像这样
发票没有, 总的, 增值税, 网络...其他领域
表格返回所有行,用户还可以说我希望对任何字段进行总计,例如我想总结所有增值税和/或所有净值,或者只是单独总计,问题是我们不知道他们想要总计哪些字段,如果我们这样做了,我们可以做
query.Sum(x => x.net)
但是我们有多个未知列的总和,因此我们尝试使用属性名称映射到如上所示的 select 语句
数据示例,
invoices Net Gross Vat
1 10 10 10
2 20 20 20
3 10 30 30
4 15 40 40
5 50 50 50
6 5 60 60
用户指定获取的总毛额和增值税 毛额 - 210,增值税 - 210 以及他们所有的结果
是的,查询按 1 分组以获得聚合来计算总和,但这是之前完成的,例如
query.groupBy(x => 1).select(insertDynamicSelectHere);
最佳答案
如果我正确理解了您的要求,那么代码如下:
public static class LinqExtensions
{
public static Expression<Func<IGrouping<int, TInput>, TOutput>> AggregateExpression<TInput, TOutput>(string[] strings) where TInput: new()
{
ParameterExpression p = Expression.Parameter(typeof(IGrouping<int, TInput>));
// Create object using Member Initialization; for example `new XXX { A = a.Sum(b => b.A), B = a.Sum(b => b.B) }`
MemberInitExpression body = Expression.MemberInit(
Expression.New(typeof(TOutput).GetConstructor(Type.EmptyTypes)),
strings.Select(CreateMemberAssignment).ToArray()
);
// Create lambda
return Expression.Lambda<Func<IGrouping<int, TInput>, TOutput>>(body, p);
// Create single member assignment for MemberInit call
// For example for expression `new XXX { A = a.Sum(b => b.A), B = a.Sum(b => b.B) }` it can be `A = a.Sum(b => b.A)` or `B = a.Sum(b => b.B)`
MemberAssignment CreateMemberAssignment(string prop)
{
// If needed you can map TInput.Prop to TOutput.Prop names here
PropertyInfo propInfo = typeof(TOutput).GetProperty(prop);
return Expression.Bind(
propInfo,
Expression.Convert(
Expression.Call(
typeof(Enumerable),
"Sum",
new[] {typeof(TInput)},
new[] {p, CreateSumLambda(prop)}
),
propInfo.PropertyType
)
);
}
// Create Lambda to be passed to Sum method
Expression CreateSumLambda(string prop)
{
ParameterExpression q = Expression.Parameter(typeof(TInput));
return Expression.Lambda(Expression.Property(q, prop), q);
}
}
}
所以不要调用
invoices.GroupBy(x => 1)
.AsQueryable()
.Select(i => new AggregatedInvoice
{
Net = i.Sum(x => x.Net),
Gross = i.Sum(x => x.Gross)
})
你可以打电话
invoices.GroupBy(x => 1)
.AsQueryable()
.Select(LinqExtensions.AggregateExpression<Invoice, AggregatedInvoice>(new[] { "Net", "Gross" }));
对于以下型号:
public class Invoice
{
public int Id { get; set; }
public decimal Net { get; set; }
public decimal Gross { get; set; }
public decimal Vat { get; set; }
}
public class AggregatedInvoice
{
public decimal? Net { get; set; }
public decimal? Gross { get; set; }
public decimal? Vat { get; set; }
}
方法接受 2 个类型参数 TInput
和 TOutput
。如果需要,您可以为两者使用相同的类。
唯一的限制是 TInput
和 TOutput
必须具有相同名称的属性。
关于c# - C# 使用表达式树生成分组后带有求和的 select 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57822785/