EF核心-为什么显式加载的速度非常慢?

时间:2018-08-17 11:24:02

标签: domain-driven-design entity-framework-core loading explicit

我正在使用域驱动设计在.Net Core中开发微服务。基础结构层具有用于访问数据库的EF Core DbContext,在我的存储库中,我具有异步方法来检索数据。

由于Include / ThenInclude不支持过滤(至少不支持Ef Core 2.1),因此我尝试了在搜寻如何替换Include时发现的所有可能方法。我也观看了有关Ef Core的Pluralsight视频,当我看到Explicit Loading选项时,由于它能够过滤相关对象,我感到非常高兴,但是当我将其中一种方法改写为Explicit版本时,查询运行了几个毫秒上升了几分钟!

在我的实体配置中,我设置了所有导航和外键,但是我不确定显式加载是否需要任何其他设置?在建议使用全局过滤器之前,请注意Where子句通常更长,因此下面的示例只是实际过滤的简化版本!

这是我的方法的样子(TransferService用作聚合,TransferServiceDetail和任何其他类只是TransferService域内的实体)

public async Task<IEnumerable<TransferService>> GetAllAsync(
    TransferServiceFilter transferServiceFilter)
    {
        int? pageIndex = null;
        int? itemsPerPage = null;

        IEnumerable<TransferService> filteredList = DBContext.TransferServices.Where(
           ts => !ts.IsDeleted); //This one itself is quick.

        //This is just our filtering, it does not affect performance.
        if (transferServiceFilter != null)
        {
            pageIndex = transferServiceFilter.PageIndex;
            itemsPerPage = transferServiceFilter.ItemsPerPage;

            filteredList = filteredList.Where(f =>
                (transferServiceFilter.TransferSupplierId == null ||
                f.TransferSupplierId == transferServiceFilter.TransferSupplierId) &&
                (transferServiceFilter.TransferDestinationId == null || 
              f.TransferDestinationId == transferServiceFilter.TransferDestinationId) &&
                (transferServiceFilter.TransferSupplierId == null ||
              f.TransferSupplierId == transferServiceFilter.TransferSupplierId) &&
                (string.IsNullOrEmpty(transferServiceFilter.TransportHubRef) ||
              f.NormalizeReference(f.TransportHubRef) == 
                 f.NormalizeReference(transferServiceFilter.TransportHubRef)));
        }

        //This is just for paging and again, this is quick.
        return await FilterList(filteredList.AsQueryable(), pageIndex, itemsPerPage);
    }

    public async Task<IEnumerable<TransferService>> GetAllWithServiceDetailsAsync(
      TransferServiceFilter transferServiceFilter)
    {
        IEnumerable<TransferService> returnList = await GetAllAsync(
           transferServiceFilter);

        //This might be the problem as I need to iterate through my TransferServices
        //to be able to load all TransferServiceDetails that belong to each individual
        //Service.
        foreach (TransferService service in returnList)
        {
            await DBContext.Entry<TransferService>(service)
              .Collection(ts => ts.TransferServiceDetails.Where(
                 tsd => !tsd.IsDeleted)).LoadAsync();
        }

        return returnList;
    }

在我的存储库中,我还有其他方法,类似地引用以前的GetAllXY ...方法(TransferServiceDetails具有Rates,Rates具有句点等)。

我的想法是仅在我只需要TransferService数据时调用GetAllAsync(并且单独使用此方法很快),或者在我还需要选定服务的详细信息时调用GetAllWithServiceDetailsAsync,等等,但是我在该父级中处于较低位置-child层次结构,执行速度变慢,而我所说的是分钟,而不仅仅是几个毫秒,或者最坏的情况是秒。

那么我的问题又是:我可能会从显式加载所需的实体配置中遗漏任何其他设置,或者只是我的查询不正确?或者,只有当只有一个TransferService作为父项而不是TransferServices列表(在我的情况下为50-100)并且只有几个与孩子相关的实体(在我的情况下,我通常有5个),显式加载才是好方法10个明细,每个明细有2-3个费率,每个费率恰好有1个期间,依此类推?)

1 个答案:

答案 0 :(得分:0)

我想您的过滤无法转换为SQL,并且所有过滤都在客户端进行(EF将所有TransferServices实体加载到内存中,在内存中过滤并丢弃不匹配的数据。)

我可以通过启用详细的(调试)日志记录来进行检查-EF会将SQL转储到日志中。

确认后,您应该进行改进:

首先,将if放到哪里。代替:

filteredList = filteredList.Where(f => transferServiceFilter.TransferSupplierId == null ||
     f.TransferSupplierId == transferServiceFilter.TransferSupplierId)

使用

if (transferServiceFilter.TransferSupplierId != null)
{
  filteredList = filteredList.Where(f => f.TransferSupplierId == transferServiceFilter.TransferSupplierId)
}

第二,您应该重新考虑NormalizeReference。无法在服务器端执行此操作,因为SQL Server不了解此实现。您应该对TransportHubRef进行预规范化,将其保存在数据库中(例如NormalizedTransportHubRef),并以简单的等式使用Where。 (此外,请不要忘记索引)。

相关问题