【问题标题】:IEnumerable extension methods called on IQueryable causing performance issues [duplicate]在 IQueryable 上调用的 IEnumerable 扩展方法导致性能问题 [重复]
【发布时间】:2019-05-01 20:34:09
【问题描述】:

每当我调用一个在 IQueryable 上接受 IEnumerable 的扩展方法时,使用该列表/实体的其余过程的速度非常慢。

POR QUEEEEE?!

问题似乎与代码的实际结构无关,因为它是最佳的,当我启动一个新数据库进行单元测试时,问题似乎没有出现。

这是扩展方法:

        public static Bill FirstById(this IEnumerable<Bill> query, int billId)
        {
            return query.FirstOrDefault(r => r.Id == billId);
        }

这就是它的使用方式:

        public Bill GetDeposit(int depositId)
        {
            var deposit = _depositRepository
                .AndSalesOrder()
                .AndSalesOrderCustomerContact()
                .AndDepositApplications()
                .FirstById(depositId);

            return deposit;
        }

这就是实际实体的使用方式:

        public bool ConvertDeposit(List<ConvertDepositSubmitModel> conversions)
        {
            if (conversions.Any())
            {
                var depositId = conversions.Select(c => c.DepositId)
                .Distinct()
                .Single();

                var billPaymentIds = conversions.Select(c => c.BillPaymentId);

                var deposit = _dataProvider.GetDeposit(depositId);

                var billPayments = _dataProvider.GetBillPayments(billPaymentIds);

                var totalConversionAmount = conversions.Sum(c => c.Amount);

                var unappliedDepositAmount = (deposit.BillStatusId == BillStatus.Credited ? 0 : deposit.TotalSell - deposit.Balance) - deposit.DepositApplications.Select(a => a.Amount).DefaultIfEmpty(0).Sum();

                if (unappliedDepositAmount != totalConversionAmount)
                {
                    throw new Exception("The provided conversion amount would not fully convert the Deposit.");
                }

                _unitOfWork.TryTransactionAndCommit(() =>
                {

                    foreach (var conversion in conversions)
                    {
                        var billPayment = billPayments.FirstByBillPaymentId(conversion.BillPaymentId);


                        this.CreateBillPaymentBill(deposit, conversion);

                        this.CreateMoneyOnAccountTransaction(deposit, conversion);

                        this.UpdateBillPayment(conversion, billPayment);
                    }

                    this.UpdateNetProceeds(billPayments);

                    this.ApplyCreditCardFees(billPaymentIds);

                    var customerCredit = this.CreateCustomerCredit(deposit, totalConversionAmount);

                    this.CreateCustomerCreditBill(deposit, customerCredit);

                    this.UpdateDeposit(deposit, totalConversionAmount);
                });
            }
            else
            {
                throw new Exception("The provided set of bill payments was empty.");
            }

            return true;
        }

我们看到,每种方法都经过严格测试,产生以下诊断结果:

PV2ANZAC                
GetDeposit: 33434ms
GetBillPayments: 54ms
CreateBillPaymentBill1: 17775ms
CreateMoneyOnAccountTransaction1: 10774ms
UpdateBillPayment1: 10810ms
UpdateNetProceeds: 18130ms
ApplyCreditCardFees: 17206ms
Insert CustomerCredit: 10795ms
CustomerCredit SaveChanges: 16276ms
CreateCustomerCredit: 27075ms
CreateCustomerCreditBill: 10688ms

而且我们绝对希望一切都比实际情况至少小一个数量级。

【问题讨论】:

  • ...当我启动一个新数据库进行单元测试... 那么您不是单元测试。单元测试没有依赖项。您正在描述集成测试。

标签: c# sql entity-framework extension-methods


【解决方案1】:

为什么要创建一个接受 IEnumerable 的扩展方法?

显然,当您在 IQueryable 上调用 IEnumerable 扩展方法时,IQueryable 将被水合,当您只需要一个时,您将引入 Bill 表中的每一行!

现在,如果我们可以假设 GetBillPayments 类似地调用数据库,这就解释了这里的差异:

GetDeposit: 33434ms
GetBillPayments: 54ms

【讨论】:

    【解决方案2】:

    检查以下两点:

    1) 您正在使用更改跟踪,这可能会导致异常缓慢。请参阅:https://weblog.west-wind.com/posts/2014/dec/21/gotcha-entity-framework-gets-slow-in-long-iteration-loops#Turn-off-Change-Tracking

    2) 您正在从数据库中提取一堆记录,然后在内存中查询该集合,而不是在数据库级别查询您想要的内容。检查 EF 发送的查询,并检查以确保您没有在存储库级别实现列表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多