2017-03-02 82 views
0

我正在通過實體框架從數據庫中檢索映射到實體類的一些元組。使用IQueryable.OrderBy作爲組合鍵而不加載所有項目?

對於這些實體,我有一個密鑰選擇器函數(由其他開發人員在運行時提供),我想傳遞給Queryable.OrderBy

public void RegisterEntity<TEntity, TKey>(string entityName, TKey defaultKey, Func<TEntity, TKey> keySelectorFunc) 

我想物化之前執行此OrderBy電話:其由一個外形大致這樣的方法來發生的那樣 - 鍵選擇功能在我的系統中的實體類型的「註冊」提供將結果映射到實體對象(即以使得OrderBy調用仍然在底層被轉換爲SQL的方式)。

問題是實體具有複合鍵,因此,鍵選擇器函數將返回在該函數中實例化的自定義對象。你可以把它想象這樣的:

var keySelectorFunc = e => new CustomKey(e.Value1, e.Value2); 

像往常一樣,實體框架不喜歡這個(通常「只有參數構造函數初始化,並在支持LINQ到實體」的錯誤)。

有沒有什麼辦法可以使用這樣的自定義按鍵選擇函數來返回自定義按鍵?我必須訴諸匿名課程嗎?或者我應該在離開LINQ-to-Entities世界之後將OrderBy電話轉移到某個地方嗎?

+0

此值(e.Value1和e.Value2)來自哪裏? –

+0

對於組合鍵的每一列,您可以使用兩個子句。即使假設您可以通過自定義鍵提供排序,SQL服務器也不會同時在兩列上執行排序。 OrderBy和ThenBy應導致正確的SQL查詢,並且在將數據返回到代碼之前應在SQL服務器上執行排序。 –

+0

@EdneyBatistadaSilva:它們是映射到數據庫列的實體的屬性。 –

回答

0

在這種特殊情況下,使用通用列表的排序方法很容易。

https://msdn.microsoft.com/en-us/library/3da4abas(v=vs.110).aspx

排序方法要求列表的類型來實現IComparable接口,它使用CompareTo方法的實現從IComparable接口。否則執行IComparer也可以傳遞給這個方法。

所以如果你的實體類已經實現了IComparable接口,那麼這應該可以爲你工作。您當然必須先對.ToList()上的IQueryable結果進行調用,然後才能對其調用Sort方法。

public class Category : IComparable<Category> 
{ 
    public int CategoryId { get; internal set; } 
    public string CategoryName { get; internal set; } 

    public int CompareTo(Category x) 
    { 
     return String.Compare(x.CategoryName, this.CategoryName, StringComparison.InvariantCulture); 
    } 
} 

List<Category> categories = new List<Category>(); 
categories.Add(new Category {CategoryName = "Cate1"}); 
categories.Add(new Category {CategoryName = "Cate2"}); 

categories.Sort(); 
foreach (var cat in categories) 
{ 
    Console.WriteLine(cat.CategoryName); 
} 

這將顯示我的類別名稱基於我已經寫在Category類CompareTo方法比較邏輯相反的順序。

0

在這種情況下,我認爲最好的方法是使用自定義ExtensionMethod來避免任何編碼開銷或不必要的複雜性。

看看它的實施可以幫助你。

首先,我們創建customkey類,它是RESPONSABLE創建語句表達式:

class CustomKey 
{ 
    public CustomKey(params string[] value) 
    { 
     if(!value.Any()) 
      throw new InvalidOperationException("Select at least one Property for this operation"); 
     Values = new List<string>(); 
     Values.AddRange(value); 
    } 

    private List<string> Values { get; set; } 

    // this method run throughout all property configured to create the expressions 
    public void ForEachProperty<TSource, TKey>(Action<Expression<Func<TSource, TKey>>, bool> method) 
    { 
     bool firstItem = true; 
     Values.ForEach(f => 
     { 
      var expression = CreateExpression<TSource, TKey>(f); 
      method(expression, firstItem); 
      firstItem = false; 
     }); 
    } 

    // this method is responsable to create each expression 
    Expression<Func<TSource, TKey>> CreateExpression<TSource, TKey>(string property) 
    { 
     var parameter = Expression.Parameter(typeof(TSource), "x"); 
     var member = typeof(TSource).GetMember(property).FirstOrDefault(); 
     Expression body = Expression.MakeMemberAccess(parameter, member);    
     return Expression.Lambda<Func<TSource, TKey>>(Expression.Convert(body, typeof(object)), parameter); 
    } 
} 

之後,我們創建自定義ExtesionMethod,somethink這樣的:

public static class OrderByExtensionClass 
{ 
    // instead of try passing an expression, we pass our CustomKey object with the columns to sort. 
    // than this method create the apropriate OrderBy Expression statement 
    public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, CustomKey customKey) 
    { 
     // the parameter isFirst is just to control where we are to build the expression 
     customKey.ForEachProperty<TSource, object>((expression, isFirst) => 
     { 
      if (isFirst) 
       source = source.OrderBy(expression); 
      else 
       source = ((IOrderedQueryable<TSource>)source).ThenBy(expression); 
     }); 
     return ((IOrderedQueryable<TSource>)source); 
    } 

} 

之後,我們只是做:

CustomKey custom = new CustomKey("Name", "Age"); 
myEntityContext.People.OrderBy(custom).ToList() 

我希望它能幫助你。

0

我認爲部分問題在於OrderBy不知道如何處理複雜類型。 SQL Server知道如何通過基本類型進行排序,但就是這樣。你將不得不做類似OrderBy(x => x.Field1).ThenBy(x => x.Field2)。您可以編寫一個擴展方法,該方法接受密鑰,從密鑰中提取屬性名稱,然後構建.OrderBy()。ThenBy()表達式,只要您在執行查詢之前知道密鑰的內容即可。否則是的,您可能需要在訂購前實現結果。

相關問題