我正在使用域驱动设计在.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个期间,依此类推?)
答案 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
。
(此外,请不要忘记索引)。