【问题标题】:Add an additional value from LINQ从 LINQ 添加附加值
【发布时间】:2019-03-14 15:24:17
【问题描述】:

我有以下代码:

var checkRegisters = await Database.Client.Context.CheckRegister
            .Where(cr => cr.CheckingAccount == account 
                   && cr.CheckDate >= fromDate 
                   && cr.CheckDate <= toDate 
                   && cr.Void == "N" )
            .GroupBy(cr => cr.CheckNumber, 
                     cr => cr.Amount, (key, g) => 
                                          new { 
                                                CheckNumber = key, 
                                                Amount = g.Sum()
                                              })
            .OrderBy(c => c.CheckNumber)
            .ToListAsync();

除了返回 CheckNumber 和 Amount 列表之外,我不完全理解它在做什么。 CheckRegister 表包含 Payee 列,EF 中相应的 modelBuilder 也是如此:

entity.Property(e => e.Payee).HasColumnType("varchar(255)");

如何将收款人添加到我返回的列表中?

【问题讨论】:

    标签: c# list entity-framework linq


    【解决方案1】:

    代码的作用:

    您的数据库有一个包含CheckRegisters 的表。每个CheckRegister 至少有属性CheckingAccountCheckDateVoidCheckNumberAmount,据你说它还有一个属性Payee

    所以你的代码是一步一步描述的。

    // Take the complete table of CheckRegisters:
    Database.Client.Context.CheckRegister
    
    // from every checkRegister in this table (every row),
    // keep only those checkRegisters that have a CheckingAccount value equal to account
    // and a CheckDate value betwen fromData and toDate
    // and a value for property Void equalt to "N"
    
    .Where(checkRegister => checkRegister.CheckingAccount == account 
                         && checkRegister.CheckDate >= fromDate 
                         && checkRegister.CheckDate <= toDate 
                         && checkRegister.Void == "N" )
    
    // Group all remaining CheckRegisters into groups with same checkNumber
    // Each group will have a property Key which has the value of the common CheckNumber
            .GroupBy(cr => cr.CheckNumber,
    
               // ElementSelector: from every CheckRegister put only the amount in each grouop 
               checkRegister => checkRegister.Amount,
    
               // ResultSelector: take every key (which we call checkNumber)
               // and all Amounts of all CheckRegisters with this CheckNumber
               // to make one new object:
               (checkNumber, amountsFromCheckRegistersWithThisCheckNumber) => new
               { 
                    CheckNumber = key, 
    
                    // To calculate property Amount: sum all amountsFromCheckRegistersWithThisCheckNumber
                    Amount = amountsFromCheckRegistersWithThisCheckNumber.Sum(),
               })
    
    // By now you have a sequence of objects, each with two properties:
    // CheckNumber: a checkNumber used in the CheckRegisters that were left after the Where
    // Amount: the sum of all Amounts of all CheckRegisters that have this CheckNumber
    
    // Finally you do some Ordering, and convert the resulting elements into a list
    .OrderBy(groupedItem => groupedItem.CheckNumber)
    .ToListAsync();
    

    现在您想要的更改:添加Payees

    问题是,返回的数据是按 CheckNumber 分组的 CheckRegisters。每个收款人都是每个 CheckRegister。如果您使用相同的 CheckNumber 将 CheckRegisters 分组,您将获得多个具有此 CheckNumber 的收款人:

    CheckNumber | Payee | ...
        1       |   A
        2       |   B
        1       |   C
        2       |   C    // note: C  is used again
        1       |   D
    

    如果我用相同的 CheckNumber 分组,你会得到:

    group CheckNumber 1 [1, A], [1,C], [1,D]
    group CheckNumber 2 [2, B], [2,C]
    

    所以每个组可能有多个员工。如果你没问题,只需省略 ElementSelector 并在 ResultSelector 中选择员工

    var result = Database.Client.Context.CheckRegister
    .Where(...)
    .GroupBy(cr => cr.CheckNumber,
    
    // ResultSelector: take every key (which we call checkNumber)
    // and all CheckRegisters with this CheckNumber
    // to make one new object:
    (checkNumber, checkRegistersWithThisCheckNumber) => new
    { 
        CheckNumber = checkNumber, 
    
        // for Employees: Select property Employee, and convert them to a List
        Employees = checkRegistersWithThisCheckNumber
                    .Select(checkRegister => checkRegister.Employee)
                    .ToList(),
    
        // Amounts: select the Amounts and Sum
        Amount = checkRegistersWithThisCheckNumber
                     .Select(checkRegister => checkRegister.Amount)
                     .Sum(),
        });
    

    【讨论】:

    • 更近了,我想我开始看到光明了。唯一的错误是CheckNumber = key 之后的“key” 名称键在当前上下文中不存在。感谢您提供评论良好的示例。关于关键错误的任何想法?
    • 哎呀,在我将标识符更改为更具描述性的名称后输入。更正了
    【解决方案2】:

    我会将它添加到您在 GroupBy 中创建的对象中。所以像下面这样更改第 3 行

    .GroupBy(cr => cr.CheckNumber, cr => cr.Amount, cr => cr.Payee, (key, g) => 
                                  new { CheckNumber = key, Amount = g.Sum(), Payee = g.Payee})
    

    请注意,group by 仅按 Number 和 Amount 分隔,因此如果您还按 Payee 分组,您应该期望结果列表更长。这也假设 Payee 是表中列的名称。

    【讨论】:

    • 这和@Oscar M 的解决方案给出了相同的错误:方法'Queryable.GroupBy..... 的类型参数不能从用法中推断出来。尝试明确指定类型参数。
    • cr 是一个对象,可能是 CheckRegister 类型吗?您可以将 Payee 作为字符串添加到该对象的字段中,然后再次尝试该代码吗?
    • 我以为我在原始帖子的最后一段代码中已经有了:entity.Property(e =&gt; e.Payee).HasColumnType("varchar(255)"); 这就是实体框架从我的数据库表中构建出来的......至少是这样我理解它可以工作
    • 我应该补充一点,这是 Entity Framework Core。在尝试了 Group By 的许多变体后仍然无法正常工作
    【解决方案3】:

    将新列添加到组中:

    var checkRegisters = await Database.Client.Context.CheckRegister
                .Where(cr => cr.CheckingAccount == account && cr.CheckDate >= fromDate && cr.CheckDate <= toDate && cr.Void == "N" )
                .GroupBy(cr => cr.CheckNumber, cr => cr.Amount, e => e.Payee , (key, g, p) => new { CheckNumber = key, Amount = g.Sum(), Payee = p})
                .OrderBy(c => c.CheckNumber).ToListAsync();
    

    【讨论】:

    • 这和@cyclopeanCity 的解决方案给出了相同的错误:方法'Queryable.GroupBy..... 的类型参数不能从用法中推断出来。尝试明确指定类型参数。
    猜你喜欢
    • 1970-01-01
    • 2010-10-16
    • 1970-01-01
    • 2012-02-26
    • 1970-01-01
    • 1970-01-01
    • 2020-12-24
    • 2015-12-20
    • 2017-08-22
    相关资源
    最近更新 更多