簡體   English   中英

我怎樣才能使這個 LINQ 查詢更有效率?

[英]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 都有屬性DateTypeIdActualDate 在此 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM