c# - C# 使用表达式树生成分组后带有求和的 select 语句

标签 c# entity-framework linq expression-trees

如果问题没有真正意义,我深表歉意,

我们在某些上下文中使用 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 个类型参数 TInputTOutput。如果需要,您可以为两者使用相同的类。 唯一的限制是 TInputTOutput 必须具有相同名称的属性。

关于c# - C# 使用表达式树生成分组后带有求和的 select 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57822785/

相关文章:

c# - 使用(FileStream)创建临时文件导致 "The process cannot access the file because it is being used"

c# - Oracle 和 T-SQL 之间 Entity Framework 中的乐观并发

c# - .NET 中的 EntityFramework - 将子实体添加到现有父实体时,正在验证/添加父实体

c# - Linq 和 EntityFramework 4 具有多个内部连接和嵌套子查询

c# - 如何将两个类合并为一个单元?

c# - 如果列表不为空,则将拦截添加到查询

c# - 如何为音频剪辑的长度实例化对象

c# - WPF 从具有双向绑定(bind)的 ViewModel 中选择 DataGrid 中的多个项目

c# - FO-DICOM:在 C# Windows 窗体应用程序中使用呈现的位图调整窗口大小会导致崩溃

entity-framework - 从数据库更新后 Context.cs 为空,.edmx 文件将其生成为空