我在使用领域驱动设计的.Net核心中从事微服务的工作。基础架构层有EF核心DbContext来访问数据库,在我的存储库中,我有异步方法来检索数据。
因为Include/ThenInclude不支持过滤(至少不支持Ef Core 2.1),所以我尝试了在googling上搜索如何替换Include时发现的所有可能的方法。我也看了关于Ef Core的Pluralsight视频,当我看到显式加载选项时,我真的很高兴,因为它能够过滤相关的对象,但当我将其中一个方法重写为显式版本时,运行了几毫秒的查询上升到了几分钟!
在我的实体配置中,我设置了所有的导航和外键,但我不确定显式加载是否需要任何额外的设置?在建议使用全局筛选器之前,请注意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有费率,费率有周期,等等)。
我的想法是当我只需要TransferService数据时简单地调用GetAllAsync (这个方法非常快),或者当我还需要所选服务的详细信息时调用GetAllWithServiceDetailsAsync,等等,但是我在这个父子层次结构中越低,执行就变得越慢,我说的是分钟,而不仅仅是额外的几毫秒,或者在最坏的情况下是秒。
所以我的问题又来了:有没有什么额外的设置是我可能在显式加载所需的实体配置中遗漏的,或者只是我的查询不正确?或者,当只有一个TransferService作为父实体而不是TransferServices列表(在我的例子中是50-100),并且只有几个子相关实体(在我的例子中,我通常有5-10个细节,每个细节有2-3个速率,每个速率恰好有一个周期,等等)时,显式加载才是好的。
发布于 2018-08-17 21:13:29
我猜你的过滤不能转换成SQL,所有的过滤都发生在客户端(EF将所有的TransferServices实体加载到内存中,在内存中过滤,并丢弃不匹配的内容)。
我可以通过启用详细(调试)日志来检查这一点- EF会将SQL转储到日志中。
确认后,应进行改进:
首先,将ifs放在Where之外。而不是:
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进行预规范化,将其保存在DB中(比如NormalizedTransportHubRef),并使用简单相等的Where。(另外,不要忘记索引)。
https://stackoverflow.com/questions/51894361
复制相似问题