【问题标题】:Optimize LINQ instead of creating new collections/loops优化 LINQ,而不是创建新的集合/循环
【发布时间】:2014-09-23 23:55:22
【问题描述】:

我有两张桌子:

发票(InvoiceID、InvoiceNumber)
Invoices_Products(InvoiceID、ProductID、IsFinalized)

我显示了所有发票的列表,并且有一些按钮可以按“已确定”或“未确定”发票进行过滤。最终发票上的每个产品都是IsFinalized==true

目前我有以下代码,它的执行速度很慢:

IEnumerable<Invoice> invoices = db.Invoices;

if (isFinalized) // filter by finalized invoices
{
    List<Invoice> unfinalizedInvoices = new List<Invoice>();

    foreach (var invoice in invoices)
    {
        int invoicesProductsCountTotal = db.Invoices_Products.Where(l => l.InvoiceID == invoice.InvoiceID).Count();
        int invoicesProductsCountFinalized = db.Invoices_Products.Where(l => l.InvoiceID == invoice.InvoiceID && l.IsFinalized == true).Count();

        if (invoicesProductsCountTotal != invoicesProductsCountFinalized)
        {
            unfinalizedInvoices.Add(invoice);
        }
    }

    invoices = invoices.Except(unfinalizedInvoices);
}
else
{
    List<Invoice> finalizedInvoices = new List<Invoice>();

    foreach (var invoice in invoices)
    {
        int invoicesProductsCountTotal = db.Invoices_Products.Where(l => l.InvoiceID == invoice.InvoiceID).Count();
        int invoicesProductsCountFinalized = db.Invoices_Products.Where(l => l.InvoiceID == invoice.InvoiceID && l.IsFinalized == true).Count();

        if (invoicesProductsCountTotal == invoicesProductsCountFinalized && invoicesProductsCountFinalized > 0)
        {
            finalizedInvoices.Add(invoice);
        }
    }

    invoices = invoices.Except(finalizedInvoices);
}

我意识到这不是最佳的,但我喜欢展开我的 LINQ,以便我可以阅读和理解它。
我的问题:有什么方法可以让这个查询更快地使用.All.Any 什么的,或者我是否需要重新考虑我的数据库设计(可能在 Invoices 表中添加一个额外的列)

编辑:第三张表是Products(ProductID,ProductNumber),但你知道

【问题讨论】:

    标签: c# performance linq


    【解决方案1】:

    目前您正在加载所有发票,然后加载每张发票的产品。这肯定会很慢(当您开始添加大量发票时,它会变得很慢)。

    您应该在 EntityFramework 中创建多对多关系。 (见example

    您的课程如下所示:

    class Invoice 
    {
        List<Product> Products {get; set;}
    }
    class Product
    {
        bool IsFinalized {get; set;}
    }
    

    现在您可以使用 LINQ 确保只执行 SQL 语句,该语句只获取您想要的数据:

    var invoices = db.Invoices.Where(i => i.Products.All(p => p.IsFinalized == finalized));
    

    【讨论】:

    • 谢谢。但我使用Invoices_Products 作为多对多链接表——它有一些列,如“数量”、“销售价格”等(顺便说一句,我没有先使用代码——先使用数据库)
    • 您仍然可以这样做,但是您定义了两个一对多关系,而不是多对多。然后你也可以用 LINQ 遍历它
    【解决方案2】:

    遍历每个Invoice 然后向数据库发出额外的请求将非常慢。让您的查询一次获取所有信息并遍历结果。

    var result = from invoice in db.Invoices
                 join invoicedProduct in db.Invoices_Products
                   on invoice.InvoiceId equals invoicedProduct.InvoiceId
                 select new
                 {
                     InvoiceId = invoice.InvoiceId,
                     ProductId = invoicedProduct.ProductId,
                     IsFinalized = invoicedProuct.IsFinalized
                 };
    
    var grpResult = from record in result
                    group record by record.ProductId into productGrp
                    select productGrp;
    
    foreach( var grp in grpResult )
    {
        Console.WriteLine( "ProductId: " + grp.Key.ToString( ) );
        Console.WriteLine( "TotalCount: " + grp.Count( ).ToString( ) );
        Console.WriteLine( "Finalized: " + grp.Where( item => item.IsFinalized ).Count( ).ToString( ) );
    }
    

    【讨论】:

    • 不要使用Join,紧跟GroupBy,你应该从一开始就使用GroupJoin
    • @Viper,了解一下
    • @Servy 有什么不同吗?第一条语句直到foreach 才会执行。从未使用过GroupJoin btw ;)
    • @Viper 会大大简化代码;是否会影响生成的 SQL 我不能说;你必须尝试一下并找出答案。无论哪种方式,我都不会感到惊讶。
    【解决方案3】:
    if (isFinalized)
    {
        invoices = invoices.Where(l => l.Invoices_Products.All(m => m.IsFinalized == true));
    }
    else
    {
        List<Invoice> finalizedInvoices = invoices.Where(l => l.Invoices_Products.All(m => m.IsFinalized == true)).ToList();
        invoices = invoices.Except(finalizedInvoices);
    }
    

    ^^ 这似乎显着提高了性能。好的,谢谢收听

    【讨论】:

      猜你喜欢
      • 2021-08-11
      • 2012-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-01
      • 1970-01-01
      • 2019-03-21
      • 2016-06-23
      相关资源
      最近更新 更多