2016-09-25 14 views
2

我宣佈這樣的詞典:如何使實體框架動態順序

private Dictionary<string, Expression<Func<Part, object>>> _orders = new Dictionary<string, Expression<Func<Part, object>>>() 
    { 
     {"Name", x => x.Name}, //string 
     {"Code", x => x.Code}, //string 
     {"EnterPrice", x => x.EnterPrice}, //decimal 
     {"ExitPrice", x => x.ExitPrice}, //decimal 
     {"IsActive", x => (bool)x.Active }, //bool 
     {"Quantity", x => x.Quantity}, //decimal 
     {"Reserved", x => x.Reserved}, //decimal 
    }; 

我嘗試使用下面的代碼,使數據:

NameValueCollection filter = HttpUtility.ParseQueryString(Request.RequestUri.Query); 
    string sortField = filter["sortField"]; 
    string sortOrder = filter["sortOrder"]; 
    Func<IQueryable<Part>, IOrderedQueryable<Part>> orderBy = x => x.OrderBy(p => p.Id); 
    if (!string.IsNullOrEmpty(sortField) && _orders.ContainsKey(sortField)) 
    { 
     bool sortMode = !string.IsNullOrEmpty(sortOrder) && sortOrder != "desc"; 
     if (sortMode) 
     { 
      orderBy = x => x.OrderBy(_orders[sortField]); 
     } 
     else 
     { 
      orderBy = x => x.OrderByDescending(_orders[sortField]); 
     } 
    } 
    return Ok(this.DbService.Query(null, filterQuery)); 

而且Query方法是:

public IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> filter = null, 
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, bool noTracking = true) 
{ 

    IQueryable<TEntity> query = DbContext.Set<TEntity>(); 
    if (filter != null) 
    { 
     query = query.Where(filter); 
    } 
    if (orderBy != null) query = orderBy(query); 
    return noTracking ? query.AsNoTracking() : query; 
} 

但是,當排序列不是string我得到以下的exce ption

"Unable to cast the type 'System.Boolean' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.","ExceptionType":"System.NotSupportedException","StackTrace":" at System.Web.Http.ApiController.<InvokeActionWithExceptionFilters>d__1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__0.MoveNext()"}

我認爲詞典的聲明和/或初始化是錯誤的,因爲如果我沒有任何排序瀏覽器設置,則默認順序將是x=>x.Id(被聲明爲inline)和它doesn」即使Idlong也不會崩潰。我可以用不同的方式聲明字典來解決我的問題嗎?

問題迎刃而解

我刪除字典和添加以下擴展其接收的字段名稱和分類模式作爲parametters由伊凡Stoev提供

public static class LinqExtension 
{ 
    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, bool ascending = true) 
    { 
     var type = typeof(T); 
     var parameter = Expression.Parameter(type, "p"); 
     PropertyInfo property; 
     Expression propertyAccess; 
     if (ordering.Contains('.')) 
     { 
      // support to be sorted on child fields. 
      String[] childProperties = ordering.Split('.'); 
      property = type.GetProperty(childProperties[0]); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 
      for (int i = 1; i < childProperties.Length; i++) 
      { 
       property = property.PropertyType.GetProperty(childProperties[i]); 
       propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); 
      } 
     } 
     else 
     { 
      property = typeof(T).GetProperty(ordering); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 
     } 
     var orderByExp = Expression.Lambda(propertyAccess, parameter); 
     MethodCallExpression resultExp = Expression.Call(typeof(Queryable), 
                 ascending ? "OrderBy" : "OrderByDescending", 
                 new[] { type, property.PropertyType }, source.Expression, 
                 Expression.Quote(orderByExp)); 
     //return source.OrderBy(x => orderByExp); 
     return source.Provider.CreateQuery<T>(resultExp); 
    } 
} 

此外解決方案工作

+0

把你的解決方案下的答案是section.don't與混吧這個問題。 – Sampath

+0

對不起!我想把它放在那裏,但在過去有人告訴我,當我爲自己的問題找到解決方案時,我應該編輯問題。 – roroinpho21

+0

如果您沒有將您的答案作爲答案發布,則其他人不能對其進行投票。另外,如果您覺得這是最好的答案,您將無法接受它作爲最佳答案。輸/輸。 – STLDeveloper

回答

1

字典定義沒問題 - 沒有好的方法來聲明它具有不同類型的值。

問題是Expression<Func<T, object>>定義爲值類型屬性生成額外的Expression.Convert。要使其與EF一起工作,必須刪除轉換表達式,並且必須動態調用相應的方法。它可以在自定義的擴展方法封裝這樣的:

public static class QueryableExtensions 
{ 
    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, Expression<Func<T, object>> keySelector, bool ascending) 
    { 
     var selectorBody = keySelector.Body; 
     // Strip the Convert expression 
     if (selectorBody.NodeType == ExpressionType.Convert) 
      selectorBody = ((UnaryExpression)selectorBody).Operand; 
     // Create dynamic lambda expression 
     var selector = Expression.Lambda(selectorBody, keySelector.Parameters); 
     // Generate the corresponding Queryable method call 
     var queryBody = Expression.Call(typeof(Queryable), 
      ascending ? "OrderBy" : "OrderByDescending", 
      new Type[] { typeof(T), selectorBody.Type }, 
      source.Expression, Expression.Quote(selector)); 
     return source.Provider.CreateQuery<T>(queryBody); 
    } 
} 

,並在方案中的使用可能是這樣的:

if (!string.IsNullOrEmpty(sortField) && _orders.ContainsKey(sortField)) 
    orderBy = x => x.OrderBy(_orders[sortField], sortOrder != "desc"); 
+0

感謝您的解決方案。我在迴歸聲明中做了一個演員,並且它工作。然而,我發現了另一個'Orderby'擴展的解決方案,我會編輯我的答案,以防有人需要它。 – roroinpho21

+0

您找到的解決方案是實現目標的另一種方式。與字典方法相比唯一的缺點是由於對屬性名稱進行了硬編碼,所以它是不安全的。 –

+0

是的你有權利。我將繼續爲該功能啓動解決方案。 – roroinpho21