.ToList() 转换花费太多时间

问题描述 投票:0回答:1

这是两个不需要时间执行的快速查询。 但是,当我将查询从 AsQueryable 转换为 .ToList() 时,只有一行需要超过两到三分钟的时间。但我需要在一秒钟内获得信息。

这是代码。请帮我解决这个问题

var apiUser = ApiCall.InstanceAPIUser();
                var allusers = (await apiUser.UsersGetAllAsync()).Data.ToList();
                IQueryable<AllDocumentDetails> documents = null;
                var currentRut = allCompanies.FirstOrDefault().Rut;
                var documentsSql = acompDbContext.Documents.ToList();

                var rutList = allCompanies.Select(c => c.Rut).ToList();
                var query1 = from d1 in documentsSql
                             join c in rutList on d1.DocRecep equals c
                             join da in acompDbContext.DocumentAssignations on d1.DocumentId equals da.DocumentId
                             join dau in acompDbContext.DocumentAssignationAssignedUsers on da.DocumentAssignationId equals dau.DocumentAssignationId
                             join drp in acompDbContext.DocumentReceivedProcesses on d1.DocumentId equals drp.DocumentId into drpGroup
                             from drp in drpGroup.DefaultIfEmpty()
                             join dc in acompDbContext.DocumentComments on d1.DocumentId equals dc.DocumentId into dcGroup
                             from dc in dcGroup.DefaultIfEmpty()
                             let assignedUser = acompDbContext.DocumentAssignationAssignedUsers
                                                  .Where(c => c.DocumentAssignationId == da.DocumentAssignationId && da.InProcess == true && d1.DocumentId == da.DocumentId)
                                                  .Select(x => x.UserId)
                                                  .Distinct()
                                                  .ToList()
                             select new AllDocumentDetails
                             {
                                 DocumentId = Convert.ToInt32(d1.DocumentId), // Include the unique identifier
                                 AssignedUser = allusers.Where(x => assignedUser.Contains(x.Id)).Select(x => new AssignedUser { UserId = x.Id, Email = x.Email }).ToList(),
                                 DocumentType = d1.DocumentTypeId,
                                 DocSeries = d1.Serie,
                                 DocNumber = d1.Nro,
                                 DocRecep = d1.DocRecep,
                                 RutTaxId = d1.EmitingRUC,
                                 IssuedDate = d1.FchEmis,
                                 DueDate = d1.ExpireDate,
                                 CompanyName = d1.LegalName,
                                 ApprovalDate = drp.ExpiringApprovalDate,
                                 State = Convert.ToInt32(drp.Status),
                                 Claim = dc != null && dc.Claim == true,
                                 Comments = dc != null && dc.DocumentCommentId != 0,
                                 Pause = drp.Status.ToString() == "5",
                                 ApprovalType = Convert.ToInt32(da.ApprovalType),
                                 ExpiredApproval = drp.ExpiredApproval
                             };

                var query2 = from d1 in documentsSql
                             join c in rutList on d1.DocRecep equals c
                             join drp in acompDbContext.DocumentReceivedProcesses on d1.DocumentId equals drp.DocumentId into drpGroup
                             from drp in drpGroup.DefaultIfEmpty()
                             join dc in acompDbContext.DocumentComments on d1.DocumentId equals dc.DocumentId into dcGroup
                             from dc in dcGroup.DefaultIfEmpty()
                             where !acompDbContext.Documents
                                      .Any(d =>
                                          d.DocumentId == d1.DocumentId &&
                                          rutList.Contains(d.DocRecep) &&
                                          d.DocumentAssignations != null &&
                                          d.DocumentAssignations.Any(da => da.DocumentId == d.DocumentId))
                             select new AllDocumentDetails
                             {
                                 DocumentId = Convert.ToInt32(d1.DocumentId), // Include the unique identifier
                                 AssignedUser = new List<AssignedUser>(),
                                 DocumentType = d1.DocumentTypeId,
                                 DocSeries = d1.Serie,
                                 DocNumber = d1.Nro,
                                 DocRecep = d1.DocRecep,
                                 RutTaxId = d1.EmitingRUC,
                                 IssuedDate = d1.FchEmis,
                                 DueDate = d1.ExpireDate,
                                 CompanyName = d1.LegalName,
                                 ApprovalDate = drp.ExpiringApprovalDate,
                                 State = Convert.ToInt32(drp.Status),
                                 Claim = dc != null && dc.Claim == true,
                                 Comments = dc != null && dc.DocumentCommentId != 0,
                                 Pause = drp.Status.ToString() == "5",
                                 ApprovalType = null,
                                 ExpiredApproval = drp.ExpiredApproval
                             };

                 documents = query1.Union(query2)
                                 .GroupBy(x => x.DocumentId) // Group by the unique identifier
                                 .Select(g => g.First())
                                 .AsQueryable<AllDocumentDetails>();

这些工作正常,但问题出在这里

return documents.ToList()

仅此代码需要超过 2-3 分钟

我想在几秒钟内执行此查询。

c# asp.net-core entity-framework-6
1个回答
0
投票

首先,这是糟糕的做法:

var documentsSql = acompDbContext.Documents.ToList();

这会将所有文档加载到内存中。然后,您可以根据内存中的列表进行查询,而不是让 EF 针对您正在查询的主表构建查询。

这条线没问题,但我会在上面加上一个

Distinct

var rutList = allCompanies.Select(c => c.Rut).Distinct().ToList();

我怀疑它很容易有重复项,列表越短越好。

现在深入研究查询。首先,我们将重点关注保持这些在 SQL 中运行,并取消手动连接。 EF 使用导航属性,其中实体具有相关实体的属性和集合,EF 将构建必要的联接来运行查询。唯一需要显式连接表的情况是松散关联。 (多个表之间隐含或共享的关系,并且数据库中没有 FK)我个人也讨厌 LINQ 格式,因此这个示例将使用 fluid。

var query1 = acompDbContext.Documents
    .Select(x => new AllDocumentDetails
        {
            DocumentId = Convert.ToInt32(x.DocumentId), 
            AssignedUser = x.DocumentAssignations.Select(da => new AssignedUser { UserId = da.Id, Email = da.Email }).ToList(),
            DocumentType = x.DocumentTypeId,
            DocSeries = x.Serie,
            DocNumber = x.Nro,
            DocRecep = x.DocRecep,
            RutTaxId = x.EmitingRUC,
            IssuedDate = x.FchEmis,
            DueDate = x.ExpireDate,
            CompanyName = x.LegalName,
            ApprovalDate = x.DocumentReceivedProcess.ExpiringApprovalDate,
            State = Convert.ToInt32(x.DocumentReceivedProcess.Status),
            Claim = x.DocumentComment.Claim, // may need a null check.
            Comments = x.DocumentComment != null && x.DocumentComment.Id != 0,
            Pause = x.DocumentReceivedProcess.Status == 5,
            ApprovalType = Convert.ToInt32(x.DocumentAssignation.ApprovalType),
            ExpiredApproval = x.DocumentReceivedProcess.ExpiredApproval
        });

如果您的文档实体上没有 DocumentReceivedPricess 或 DocumentComment 等,那么如果您想使用实体框架,这是您应该调查并寻求解决的第一个问题。这些是应该用于表达这些相关实体之间的关系的导航属性。很多时候,项目是用它们建立的,开发人员只是不太清楚如何/为什么使用它们。它们不仅适用于取回实体和相关数据,还用于投影。 (使用

Select

您可以通过以下方式进行初步测试:

var results = query1.ToList();

...确保有效并返回您期望的结果。从那里您应该能够类似地计算出 query2,而无需使用除 rutList 之外的内存列表。

结果数据应该是:

var results = query1.Union(query2)
    .GroupBy(x => x.DocumentId)
    .Select(g => g.First());

虽然我不确定为什么你只想要第一个文档的分组结果。这应该会让您朝着正确的方向来整理这些查询。一旦关系和预测发挥作用,如果您仍然遇到性能问题,那么下一步就是考虑进一步的过滤(当您似乎不需要所有文档时,这似乎仍然会遍历all文档)和索引在数据库中。

© www.soinside.com 2019 - 2024. All rights reserved.