简体   繁体   English

在 EfCore 的相关模型中通过 Sum 聚合有效地查询/排序

[英]Efficiently querying/ordering by Sum aggregate in related model in EfCore

I'm still fairly new to C#, .NET and EfCore so please bear with me and apologies if I'm missing something in the question.我对 C#、.NET 和 EfCore 还很陌生,所以如果我在问题中遗漏了什么,请多多包涵并道歉。

Let's say I have the following relations假设我有以下关系

public class User
{
    public Account Account {get; set;}
    public string Foo {get; set;}

}

public class Account
{
    public List<Transaction> Transactions {get; set;}
}

public class Transaction
{
    public decimal Amount {get; set;}
    public TransactionType TransactionType {get; set;}

}

public enum TransactionType 
{
    Credit = 1,
    Debit = 2,
    Refund = 3,
}

I'd like to work with balances, which need to be calculated every time the User model is retrieved.我想使用余额,每次检索用户模型时都需要计算余额。 For this example, let's say I need to order a list of users, where Foo is "Bar", by their account balance对于这个例子,假设我需要按用户的帐户余额排序一个用户列表,其中 Foo 是“Bar”

var query = db.Users
    .Include(u => u.Account)
    .ThenInclude(a => a.Transactions)
    .Where(u => u.Foo == "Bar");

var orderedQuery = query
         .OrderByDescending(u => 
              (u.Account.Transactions
                .Where(t => t.TransactionType == TransactionType.Credit || t.TransactionType == TransactionType.Refund)
                .Sum(t => t.Amount)) 
              - u.Account.Transactions
                 .Where(t => t.TransactionType == TransactionType.Debit)
                 .Sum(t => t.Amount)
              )
);


// Build the List
return orderedQuery.Skip(...).Take(...).Select(x => new SomeDTO{/* Build some DTO's with User Info, Account Info and Balance */}).ToList();

The above works, but is inefficient.上述工作,但效率低下。

I'm working on a fairly large codebase and existing database in a asp.net core & sql-server project, so I need to work with what I've got.我正在一个 asp.net 核心和 sql-server 项目中处理一个相当大的代码库和现有数据库,所以我需要使用我所拥有的。 Can anyone suggest a better way of doing this kind of work?任何人都可以建议一种更好的方式来完成这种工作吗?

Do you really need all these Include s?你真的需要所有这些Include吗? They are not necessary for the ordering aggregation later.以后的排序聚合不需要它们。

var query = db.Users
    .Include(u => u.Account)
    .ThenInclude(a => a.Transactions)
    .Where(u => u.Foo == "Bar");

You can optimize the ordering part of the query, by combining into a single grouping您可以通过组合成一个分组来优化查询的排序部分

var orderedQuery = query.OrderByDescending(u =>
    u.Account.Transactions
    .GroupBy(t => 1)
    .Select(g =>
          g.Where(t => t.TransactionType == TransactionType.Credit || t.TransactionType == TransactionType.Refund)
           .Sum(t => t.Amount)
        - g.Where(t => t.TransactionType == TransactionType.Debit)
           .Sum(t => t.Amount)
        )
    );

Skip Take is pretty inefficient, as it requires searching through the whole list again. Skip Take效率很低,因为它需要再次搜索整个列表。 It may be prudent to cache it all in the client and page it afterwards.谨慎的做法是将其全部缓存在客户端中,然后再对其进行分页。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM