[英]How could I make this LINQ query more efficient?
關於如何使這個 LINQ 查詢更有效率的任何建議? 我最近創建了 formattedDate 變量,因為我之前多次調用 GetDateInFormat。
if (customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.Where(ol => ol.DateTypeId == "OrderPickUpFrom" && ol.ActualDate != null)
.Any())
{
var date = customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.OrderBy(d => d.ActualDate)
.FirstOrDefault(d => d.DateTypeId == "OrderPickUpFrom" && d.ActualDate != null)
.ActualDate;
if (date != null)
{
var formattedDate = _dateHelper.GetDateInFormat("DD/MM/YYYY", date);
order.ArriveDate = formattedDate;
order.EarliestShipDate = formattedDate;
order.EarliestDeliveryDate = formattedDate;
order.EarliestApptTime = _dateHelper.GetDateInFormat("HHMM", date);
}
}
customerOrder 是客戶訂單 class。
public class Order
{
public Order();
public List<OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
public OrderLine();
public List<OrderDate> OrderDates { get; set; }
}
public class OrderDate
{
public OrderDate();
public DateTimeOffset? ActualDate { get; set; }
public string DateTypeId { get; set; }
}
出色地,
首先,讓我們去掉連續的兩個Linq 查詢: Any()
然后FirstOrDefault
。 我們可以只執行FirstOrDefault
(或其等效項),如果我們得到null
,我們就會知道Any
返回false
(即我們沒有項目)。
其次,使用OrderBy
我們對整個可枚舉對象進行排序,然后我們刪除除一項以外的所有項。 我們在浪費資源。 Aggregate
是更經濟的方式:我們不需要排序,而是掃描枚舉並返回null
或最小ActualDate
。
代碼:
var date = customerOrder
.OrderLines
.SelectMany(ol => ol.OrderDates)
.Where(ol => ol.DateTypeId == "OrderPickUpFrom" && ol.ActualDate != null)
.Select(ol => ol.ActualDate)
.Aggregate((DateTimeOffset?) null,
(s, a) => !s.HasValue || s.Value > a ? a : s);
if (date.HasValue) {
var formattedDate = _dateHelper.GetDateInFormat("DD/MM/YYYY", date);
order.ArriveDate = formattedDate;
order.EarliestShipDate = formattedDate;
order.EarliestDeliveryDate = formattedDate;
order.EarliestApptTime = _dateHelper.GetDateInFormat("HHMM", date);
}
首先,如果您不首先調用數據庫兩次,那么您可以將執行時間縮短近一半,您可以在 if 中調用它:
customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.Where(ol => ol.DateTypeId == "OrderPickUpFrom" && ol.ActualDate != null).Any())
而不是在真正的分支中:
var date = customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.OrderBy(d => d.ActualDate)
.FirstOrDefault(d => d.DateTypeId == "OrderPickUpFrom" && d.ActualDate != null).ActualDate;
如果你看看它們實際上是相同的,你可以使用這個獲得相同的結果:
var date = customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.OrderBy(d => d.ActualDate)
.Where(d => d.DateTypeId == "OrderPickUpFrom" && d.ActualDate != null)
.Select(x => x.ActualDate)
.FirstOrDefault();
現在 null 日期值將作為 if 語句的基礎
if(date != null) {
// do your stuff
}
除此之外,您可以在數據庫上創建一個索引來覆蓋您的查詢,因此包含 DataTypeId 和 ActualDate。
我建議使用.SelectMany()
、 .Where()
和.Min()
如下:
var date = customerOrder.OrderLines
.SelectMany(ol => ol.OrderDates)
.Where(ol => ol.DateTypeId == "OrderPickUpFrom")
.Min(ol => ol.ActualDate);
當.Where()
的結果為空時, .Min()
將返回null
。
所以你有一個customerOrder
。 此 customerOrder 有零個或多個OrderLines
。 每個 OrderLine 都有零個或多個OrderDates
。
從 CustomerOrder 中,您只對所有OrderDates
感興趣。 Order 或 OrderLines 的 rest 對您來說不感興趣。
每個 OrderDate 都有屬性DateTypeId
和ActualDate
。 在此 customerOrder 內部提到的所有 OrderDate 中,您想要查看是否至少有一個 OrderDate 的屬性 DateTypeId 的值等於“OrderPickUpFrom”,並且屬性 ActualDate 的值不是 null。
如果根本沒有 OrderDate 匹配,則您沒有定義您想要的內容。
如果其中一個 OrderDate 符合此要求,則取最早匹配的 OrderDate 的 ActualDate,如果沒有匹配的 OrderDate,則取 null
您使用
Any()
檢查是否至少有一個 OrderDate 的屬性 DateTypeId 的值等於“OrderPickUpFrom”,並且屬性 ActualDate 的值不是 null。因此您知道至少有一個匹配的 OrderDate,並且因此至少有一個匹配的 ActualDate。 因此你知道date
永遠不是 null。
const string requiredDateTypeId = "OrderPickupFrom";
var date = customerOrder.OrderLines
.SelectMany(orderLine => orderline.OrderDate)
.Where(orderDate => orderDate.DateTypeId == requiredDateTypeId
&& orderDate.ActualDate != null)
.OrderBy(orderDate => orderDate.ActualDate)
.Select(orderDate => orderDate.ActualDate)
.FirstOrDefault();
言下之意:
從 customerOrder 中,將此 customerOrder 的所有 OrderLines 中的所有 OrderDates 組成一個大序列。
僅保留屬性 DateTypeId 的值等於所需的 DateTypeId 且 ActualDate 具有非空值的那些 OrderDate。
按 ActualDate 的升序對剩余的 OrderDates 進行排序。 所以具有最舊 ActualDate 的 OrderDate 是第一個
從排序的 OrderDates 中,select 屬性 ActualDate 的值
保留第一個,如果沒有任何具有所需值的 OrderDate,則保留 null。
if (date.= null) { var formattedDate = 等。
現在,您可以做一件事來提高 LINQ 的效率:如果您只打算使用具有最舊 ActualDate 值的訂單日期,為什么還要對所有訂單日期進行排序。 換句話說:如果找到最舊的,則不需要對最舊之后的任何 OrderDates 進行排序。 事實上,您想要一些Min
function,您可以在其中定義用於查找最小值的屬性。
為此,我使用了一種擴展方法。 如果您不熟悉擴展方法,請閱讀擴展方法揭秘
public static MinOrDefault<T, TProperty> Min(this IEnumerable<T> source,
Func<T, TProperty> propertySelector)
{
return MinOrDefault(source, propertySelector, null);
}
public static MinOrDefault<T, TProperty> Min(this IEnumerable<T> source,
Func<T, TProperty> propertySelector,
IComparer<TProperty> comparer)
{
// TODO: check source and propertySelector not null
if (comparer == null) comparer = Comparer<TProperty>.Default;
using (IEnumerator<T> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
// There is at least one element in source
T minValue = enumerator.Current;
TProperty minPropertyValue = propertySelector(minValue);
// check the rest of the sequence
while (enumerator.MoveNext())
{
// there is a next. Is the property smaller?
T value = enumerator.Current;
TProperty propertyValue = propertySelector(value);
if (comparer.Compare(propertyValue, minPropertyValue) < 0)
{
// propertyValue is smaller than minPropertyValue
minValue = value;
minPropertyValue = propertyValue;
}
// else: minValue is still the smallest; do nothing
}
// if here, enumerated to the end of the sequence
return minValue;
}
else
{
// empty sequence, return default:
return default(T);
}
}
}
您的代碼將是:
OrderDate oldestOrderDate = customerOrder.OrderLines
.SelectMany(orderLine => orderline.OrderDate)
.Where(orderDate => orderDate.DateTypeId == requiredDateTypeId
&& orderDate.ActualDate != null)
.MinOrDefault(orderDate => orderDate.ActualDate);
if (oldestOrderDate != null)
{
etc.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.