【问题标题】:How to apply filtering for specific column?如何对特定列应用过滤?
【发布时间】:2021-10-06 20:54:50
【问题描述】:

我有如下查询:

var query = from operation in dbContext.Operations
select new OperationsDto
{
    Id = o.Id,
    ProcessDate = o.ProcessDate,
    Amount = o.Credit
    //Balance = ...
};

Operation 实体中没有 Balance 属性。我必须计算一下。 余额 => (操作总和在当前操作 ProcessDate 之前发生的操作的金额) + 当前金额)。例如:

Id      ProcessDate     Amount      Balance
1       2021.02.01      +100$       50 + 100 = +150$ (
2       2021.02.03      -200$       150$ + (-200) = -50$ (this get sum of amount where record's process date before 2021.02.03)
3       2019.01.01      +50$        0 + 50$ = 50$ (because this is first operation. there is not record before 2019.01.01)

我想在 EF Core 中使用它。 我知道我可以在检索如下数据后使用 foreach 执行此操作:

var operations = query.ToList();
foreach (var operation in operations)
{
    operation.Balance = operations.Where(x => x.ProcessDate < operation.ProcessDate).Sum(o => o.Debit - o.Credit);
}

但我需要在查询中计算

【问题讨论】:

  • 您不能在 Linq 中真正做到这一点,而且您的 foreach 循环效率极低。你真正想做的是保持“运行总数”,所以我建议你去阅读。您可以使用一些原始 SQL 来执行此操作,或者在按日期对结果进行排序后使用 this 之类的内容。
  • 所以您实际上是在谈论与每个操作相关的运行余额。初始余额好像是从第一条记录设置的?
  • @NetMage 是的,先根据processDate记录

标签: c# sql entity-framework linq entity-framework-core


【解决方案1】:

可以在查询中建立平衡,但坦率地说,它可能会非常低效,因为它需要针对操作重新查询。比如:

var query = dbContext.Operations
    .Select(o => new OperationsDto
    {
        Id = o.Id,
        ProcessDate = o.ProcessDate,
        Credit = o.Credit,
        Debit = o.Debit,
        Balance = dbContext.Operations.Where(x => x.Id != o.Id && x.ProcessDate <= o.ProcessDate).Sum(x => x.Credit - x.Debit)
    });

更快的解决方案是在内存中建立平衡:

var operations = query.OrderBy(o => o.ProcessDate).ToList();
decimal balance = 0;
foreach (var operation in operations)
{
    balance += operation.Credit - operation.Debit;
    operation.Balance = balance;
}

这仅在您加载整个范围时才有效。相反,如果您想在特定日期之后查询数据,您可以获取初始余额并从那里开始:

var operations = query
    .Where(o => o.ProcessDate >= startDate)
    .OrderBy(o => o.ProcessDate).ToList();

decimal balance = dbContext.Operations
    .Where(o => o.ProcessDate < startDate)
    .Sum(o => o.Credit - o.Debit); // starting balance.

// I can't check if the above is allowed in EF, may need to do the below:
var sums = dbContext.Operations
    .GroupBy(o => true)
    .Where(o => o.ProcessDate < startDate)
    .Select(o new 
    {
        CreditSum = o.Sum(x => x.Credit),
        DebitSum = o.Sum(x => x.Debit)
    }).Single();
var balance = sums.CreditSum - sums.DebitSum; // starting balance.

foreach (var operation in operations)
{
    balance += operation.Credit - operation.Debit;
    operation.Balance = balance;
}

【讨论】:

    猜你喜欢
    • 2021-10-29
    • 2022-01-26
    • 1970-01-01
    • 1970-01-01
    • 2021-07-22
    • 2018-11-30
    • 2016-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多