c# - 可怕又大的 LINQ 语句优化

标签 c# performance entity-framework linq

我必须向数据库发出相当大的请求以获取大量数据,但是它需要很长时间才能运行。有什么方法可以提高性能吗?先发制人地为丑陋的代码道歉(我确实有一个版本将它分成多个较小的函数,但速度更慢)

from contact in _database.OBJECTCONTACT
                where contact.OBJECTCONTACTOWNER.Any(o => o.OBJECTID == id && o.OBJECTTYPE == type) && contact.ACTIVE >= 1 && CheckViewAccess(contact)
                group contact by (contact.OBJECTCONTACTPROJECT.Any() ? contact.OBJECTCONTACTPROJECT.First().OBJECTPROJECT.PROJECTNAME : "General") into projectGroup
                select new ProjectViewModel()
                {
                    ProjectName = projectGroup.Key,
                    ContactGroups = (from g in _database.OBJECTGROUP
                            where g.GROUPTYPE == "CONTACT" && ContactsModule.CheckUserRole("View", g.OBJECTTYPE, g.GROUPNAME)
                                  select new ContactGroupViewModel()
                                  {
                                      CanEdit = ContactsModule.CheckUserRole("Edit", g.OBJECTTYPE, g.GROUPNAME),
                                      GroupId = g.OBJECTGROUPID,
                                      GroupName = g.GROUPNAME,
                                      Contacts = (from c in projectGroup
                                              join l in _database.OBJECTCONTACTLOCATION on c.OBJECTCONTACTLOCATIONID equals l.OBJECTCONTACTLOCATIONID into lgrp from loc in lgrp.DefaultIfEmpty(null)
                                              orderby c.NAME
                                              select new ContactViewModel()
                                              {
                                                  Id = (int)c.OBJECTCONTACTID,
                                                  Name = c.NAME,
                                                  Description = c.DESCRIPTION,
                                                  ContactInformation = CreateContactInfoViewmodels(c),
                                                  Owners = c.OBJECTCONTACTOWNER.Where(owner => owner.OBJECTTYPE == "AIRPORT")
                                                      .Select(owner => ContactOwnerViewModel.FromOwnerId(owner.OBJECTID, owner.OBJECTTYPE)).ToList(),
                                                  Projects = c.OBJECTCONTACTPROJECT.Select(proj => proj.OBJECTPROJECT).ToList(),
                                                  Typename = GetTypeName(c),
                                                  TypeId = c.OBJECTCONTACTTYPEID ?? 0,
                                                  ContactGroupId = c.OBJECTGROUPID,
                                                  ContactGroup = g.GROUPNAME,
                                                  Editable = CheckAccessBool("EDIT", c),
                                                  Location = loc != null ? new LocationViewModel()
                                                  {
                                                      Address = loc.ADDRESS,
                                                      GoogleMapLink = loc.GMAPADDRESS,
                                                      LocationId = loc.OBJECTCONTACTLOCATIONID,
                                                      LatLon = Tuple.Create(loc.LATITUDE, loc.LONGITUDE)
                                                  } : null,
                                              }).ToList()
                                  }).ToList()
                }).ToList();

我认为我应该能够使用连接将整个数据库获取代码移动到顶部(理论上提高性能)但我无法找到适合我需要的语法

最佳答案

感谢大家提出建议。我所处的情况是我无法对数据库本身做很多事情,所以我正在充分利用我所拥有的。关于我可以使用的工具,我的手有点束手无策(也是相当旧的代码库,我认为它是 EF 5 或类似的东西)

此版本将数据库事务移至顶部(因此提取次数较少)并在底部进行大量数据操作。

// general object is created above
var res = (from contact in _database.OBJECTCONTACT.AsEnumerable() // as enumerable used to allow for defaultifempty in join (minor damage to performance)
                join oGroup in _database.OBJECTGROUP on contact.OBJECTGROUPID equals oGroup.OBJECTGROUPID into og from objectGroup in og.DefaultIfEmpty(defaultValue: general)
                where contact.OBJECTCONTACTOWNER.Any(o => o.OBJECTTYPE == type && o.OBJECTID == id)
                // ReSharper disable once PossibleNullReferenceException (it's taken care of by check using .any() )
                group new {contact, objectGroup } by (contact.OBJECTCONTACTPROJECT.Any() ? contact.OBJECTCONTACTPROJECT.FirstOrDefault().OBJECTPROJECT.PROJECTNAME : "General") into pGroup
                orderby pGroup.Key == "General" ? pGroup.Key : "􏿽" descending 
                select new ProjectViewModel()
                {
                    ProjectName = pGroup.Key,
                    ProjectId = pGroup.FirstOrDefault() != null ? (pGroup.FirstOrDefault().contact.OBJECTCONTACTPROJECT.FirstOrDefault() != null ? pGroup.FirstOrDefault().contact.OBJECTCONTACTPROJECT.FirstOrDefault().OBJECTPROJECTID : -1) : -1,
                    ContactGroups = (from c in pGroup
                            group c by c.objectGroup into grp
                            let canEdit = ContactsModule.CheckUserRole("EDIT", grp.Key.OBJECTTYPE, grp.Key.GROUPNAME)
                            orderby grp.Key.SORTORDER descending 
                            select new ContactGroupViewModel()
                            {
                                GroupName = grp.Key.GROUPNAME,
                                GroupId = grp.Key.OBJECTGROUPID,
                                CanEdit = canEdit,
                                Contacts = grp.Select(item => new ContactViewModel()
                                {
                                    Id = (int)item.contact.OBJECTCONTACTID,
                                    Name = item.contact.NAME,
                                    Description = item.contact.DESCRIPTION,
                                    Editable = canEdit,
                                    ContactInformation = item.contact.OBJECTCONTACTNUMBER.OrderByDescending(num => num.ISMAININFO).Select(num => new ContactInfoViewmodel()
                                    {
                                        Data = num.NUMBERDATA,
                                        IsMain = num.ISMAININFO > 0,
                                        Type = num.OBJECTCONTACTNUMBERTYPE.NAME
                                    }).ToList()
                                }).ToList()
                            }).ToList()
                }).ToList();

这似乎(平均)花费了原始查询所需时间的四分之一(由于数据库的大小,这仍然是一个明显的时间,但在可接受的限度内)

关于c# - 可怕又大的 LINQ 语句优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51538003/

相关文章:

C# 为什么这里不需要显式强制转换?

c# - ServiceStack V4 API 文档缺少响应类型(在列表中)

python - 从 parquet 读取时,dask dataframe 列重命名很慢(呃)

c# - 当值不相同时违反主键

asp.net - 如何使用 LINQ/Entities/VB.Net 从 SQL Server 获取 CurrentTime

c# - native C Dll 调用 C++/CLI 混合模式 Dll - 未处理的异常

c# - 在 C# 中重用数组

c#优化掉一些日志语句

ios - 当我的应用程序启动时,我可以加载所有选项卡的数据吗?

c# - 需要帮助使用 Entity Framework 导航属性过滤 LINQ 查询