我正在使用 EF 核心的 asp .net 核心 Web API。我寫了這個查詢。但這需要 20-30 秒才能執行。
任何人都有改進此查詢的想法。
var hotels = await _context.Hotels
.Where(i => (i.DestinationCode == request.Destination))
.Select(i => new HotelListHotelVm
{
Item1 = i.Item1,
Item2 = i.Item2,
Item3 = i.Item3,
Item4Code = i.Item4Code,
Item4Description = i.Item4.TypeDescription,
Item5 = i.Item5.Select(x => new HotelListHotelVm.HotelListItem5Vm
{
Code = x.Item5Code,
Description = x.Item5.Description,
}).Where(x =>(incomingItem5s.Length > 0 ) ? (incomingItem5s.Contains(x.Code)) : (x.Code != "")),
Item6 = i.Item6.Select(x => new HotelListHotelVm.HotelListHotelItem6Vm
{
Id = x.Id,
Item6TypeCode = x.Item6TypeCode,
Order = x.Order,
Path = x.Path,
VisualOrder = x.VisualOrder,
}).Take(3),
HotelFacilities = i.Facilities.ToList().Distinct().Take(6).Select(x => new HotelListHotelVm.HotelListFacilityVm {
Id = x.Id,
FacilityGroupCode = x.FacilityGroupCode,
HotelFacilityGroupDescription = x.FacilityGroup.Description,
FacilityCode = x.FacilityCode
}),
})
.Where( i => ((incomingItem4.Length > 0 ) ? (incomingItem4.Contains(i.Item4Code)) : (i.Item4Code != "")) )
.OrderByDescending(i => i.Code)
.PaginatedListAsync(request.PageNumber, request.PageSize);
foreach( var item in hotels.Items){
foreach(var facility in item.HotelFacilities){
foreach( var fac in _context.Facilities){
if(facility.FacilityCode == fac.Code){
facility.HotelFacilityDescription = fac.Description;
}
}
}
}
如果我洗掉了那些 foreach 代碼,查詢需要 8-10 秒才能執行。但我需要那些 foreach 代碼。因為我需要HotelFacilityDescription
任何優化查詢的建議?
編輯i.Facilities- 模型
public class HotelFacility
{
// removed some
public int FacilityCode { get; set; }
public int FacilityGroupCode { get; set; }
public FacilityGroup FacilityGroup { get; set; }
public int HotelCode { get; set; }
public Hotel Hotel { get; set; }
}
}
uj5u.com熱心網友回復:
_context.Facilities將針對先前回圈的每次迭代進行列舉(即將呼叫資料庫)。快速修復是呼叫它并將結果存盤在變數中:
var facilities = _context.Facilities.ToList();
foreach( var item in hotels.Items){
foreach(var facility in item.HotelFacilities){
foreach(var fac in facilities){
if(facility.FacilityCode == fac.Code){
facility.HotelFacilityDescription = fac.Description;
}
}
}
}
下一個改進可以轉換facilities為Dictionary用于搜索目的。
更好的方法可以是_context.Facilities在資料庫端撰寫查詢連接(但這里需要更多資訊)。
uj5u.com熱心網友回復:
我已經讀了幾次,但看起來 Hotel.Facilities 的關系是一個 Facility,所以你不能這樣做:
HotelFacilities = i.Facilities.ToList().Distinct().Take(6).Select(x => new HotelListHotelVm.HotelListFacilityVm {
Id = x.Id,
FacilityGroupCode = x.FacilityGroupCode,
HotelFacilityGroupDescription = x.FacilityGroup.Description,
FacilityCode = x.FacilityCode,
HotelFacilityDescription = x.Description
}),
如果由于某種原因 Hotel.Facilities 不是指向設施,而是指向設施組的多對多 HotelFacilityGroup 物體,該物體還包含設施代碼,如果關聯的設施組可以訪問其下的一組設施,您可以利用那:
編輯:聽起來多個設施共享相同的代碼,其中一些可能具有空描述。前提是與代碼匹配的設施將在同一設施組內,而不考慮不同設施組內的相同代碼。如果您需要在所有設施中匹配代碼,那么除了加載整套設施代碼和描述之外,可能沒有太多替代方法。
HotelFacilities = i.Facilities.ToList().Distinct().Take(6).Select(x => new HotelListHotelVm.HotelListFacilityVm {
Id = x.Id,
FacilityGroupCode = x.FacilityGroupCode,
HotelFacilityGroupDescription = x.FacilityGroup.Description,
FacilityCode = x.FacilityCode,
HotelFacilityDescription = x.FacilityGroup.Facilities.Where(f => f.Code == x.FacilityCode && f.Description != null).Select(f => f.Description).FirstOrDefault()
}),
這將避免需要加載所有工具來決議該代碼。否則,如果您確實需要獲取所有設施,預加載它們將是可行的方法,但與其獲取整個設施物體,我只會推薦您需要的值,代碼和描述。這減少了所需的記憶體量,并可能是一個更快的查詢:
var facilities = _context.Facilities
.Select(f => new
{
f.Code,
f.Description
}).ToList();
編輯:從那里,使用以下方法查找匹配項:
foreach( var facility in hotels.Items.SelectMany(x => x.HotelFacilities)
{
facility.HotelFaciltyDescription = facilities
.Where(x => x.Code == facility.FacilityCode
&& !string.IsNullOrEmpty(x.Description)
.Select(x => x.Description)
.FirstOrDefault();
}
我建議使用 OrderBy 子句來確保設施的選擇是可預測的,因為聽起來可能在具有非空描述的代碼上有多個匹配項。
uj5u.com熱心網友回復:
可以通過投影 LINQ to Entities 查詢中的值來消除回圈。
如果您像其他*Code欄位一樣擁有關系和導航屬性,那將會很容易。但是正如評論中澄清的那樣,沒有這種關系,所以你必須求助于舊的好手冊 left other join 來模擬導航屬性自動提供的內容,例如
HotelFacilities = i.Facilities.ToList().Distinct().Take(6)
// left outer join with Facilities
.SelectMany(x => _context.Facilities
.Where(f => x.FacilityCode == f.Code).DefaultIfEmpty(),
(x, x_Facility) => new HotelListHotelVm.HotelListFacilityVm
{
Id = x.Id,
FacilityGroupCode = x.FacilityGroupCode,
HotelFacilityGroupDescription = x.FacilityGroup.Description,
FacilityCode = x.FacilityCode,
HotelFacilityDescription = x_Facility.Description // <--
}),
如果存在,這里x_Facility模擬可選的參考導航屬性x.Facility。
如果您只需要相關表中的單個屬性,而不是左連接,您還可以使用原始查詢和單個值回傳投影內的相關子查詢,例如
HotelFacilities = i.Facilities.ToList().Distinct().Take(6)
.Select(x => new HotelListHotelVm.HotelListFacilityVm
{
Id = x.Id,
FacilityGroupCode = x.FacilityGroupCode,
HotelFacilityGroupDescription = x.FacilityGroup.Description,
FacilityCode = x.FacilityCode,
HotelFacilityDescription = _context.Facilities
.Where(f => x.FacilityCode == f.Code)
.Select(f => f.Description)
.FirstOrDefault() // <--
}),
甚至
HotelFacilityDescription = _context.Facilities
.FirstOrDefault(f => x.FacilityCode == f.Code).Description
所有這些都將消除回圈后執行額外資料庫查詢的需要。您可以測驗它們并采用性能最佳的那個(#2 和 #3 產生一個相同的 SQL,所以這是一個品味問題 - 選擇在 #1 和 #2/3 之間)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/380054.html
