可怕的和大的LINQ语句优化

时间:2018-07-26 11:42:17

标签: 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();

我认为我应该能够使用联接将整个数据库获取代码移到顶部(从理论上提高性能),但是我找不到适合自己需求的语法

1 个答案:

答案 0 :(得分:0)

感谢大家提出建议。我处在无法对数据库本身做太多事情的情况下,因此我会尽我所能。关于我所使用的工具,我的双手有点束缚(也是相当老的代码库,我认为它是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();

(平均)这大约需要原始查询时间的四分之一(由于数据库的大小,但仍在可接受的范围内,这仍然是一个明显的时间)