2017-02-03 17 views
2

Это вопрос по моему вопросу Dynamic Expression Generation Issues with ValueTypes добавление новой переменной: Entity Framework. Теперь, когда я могу генерировать необходимые выражения при работе с ValueTypes, у меня возникает новая проблема, когда Linq-to-Entities пытается обработать запрос. Я получаю следующее сообщение об ошибке:Вставка значений с использованием Linq в объекты

System.NotSupportedException: Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.

По-видимому, Linq-to-Entities не является поклонником значений в штучной упаковке. Это работает, когда я принудительно обрабатываю запрос (через ToList() или другой метод типа), но не тогда, когда это делается с помощью базы данных, которая была бы идеальной.

Есть ли способ сделать этот метод более универсальным, чтобы сделать Linq-to-Entities счастливым? Имейте в виду, что я не знаю тип свойства до выполнения.

public IEnumerable<Expression<Func<T, object>>> GetExpressions<T>(string sortedColumn) where T : IReportRecord 
{ 
    var columns = GetFullSortOrder(sortedColumn); 
    var typeParameter = Expression.Parameter(typeof(T)); 
    foreach (var c in columns) 
    { 
     var propParameter = Expression.Property(typeParameter, c); 
     if (propParameter.Type.IsValueType) 
     { 
      var boxedPropParameter = Expression.Convert(propParameter, typeof(object)); 
      yield return Expression.Lambda<Func<T, object>>(boxedPropParameter, typeParameter); 
     } 
     else 
     { 
      yield return Expression.Lambda<Func<T, object>>(propParameter, typeParameter); 
     } 
    } 
} 
+1

Вместо генерации 'Expressions' вы можете применять ваш заказ непосредственно к' IQueryable' динамически следующим образом: http://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet/233505#233505 – Aducci

ответ

2

На самом деле этот вопрос является противоположностью предыдущего, где требуется Expression.Convert при создании Expression<Func<T, object>>. Рассмотрим метод со следующей подписью

public static IOrderedQueryable<T> OrderBy<T>(
    this IQueryable<T> source, 
    IEnumerable<Expression<Func<T, object>>> selectors) 

, который должен создать OrderBy/ThenBy цепочку из пройденных селекторов. Здесь вам нужно удалитьExpression.Convert необходимо только для того, чтобы сделать возможным преобразование Expression<Func<T, V>> в Expression<Func<T, object>> для значения типа V.

Пусть создать небольшие вспомогательные методы для обоих преобразований:

public static Expression Wrap(this Expression source) 
{ 
    if (source.Type.IsValueType) 
     return Expression.Convert(source, typeof(object)); 
    return source; 
} 

public static LambdaExpression Unwrap<T>(this Expression<Func<T, object>> source) 
{ 
    var body = source.Body; 
    if (body.NodeType == ExpressionType.Convert) 
     body = ((UnaryExpression)body).Operand; 
    return Expression.Lambda(body, source.Parameters); 
} 

Теперь реализация оригинального метода может быть просто

public static IEnumerable<Expression<Func<T, object>>> GetExpressions<T>(string sortedColumn) where T : IReportRecord 
{ 
    var typeParameter = Expression.Parameter(typeof(T)); 
    return from c in GetFullSortOrder(sortedColumn) 
      select Expression.Lambda<Func<T, object>>(
       Expression.Property(typeParameter, c).Wrap(), typeParameter); 
} 

и способ применения может быть что-то вроде этого

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, IEnumerable<Expression<Func<T, object>>> keySelectors) 
{ 
    var result = source.Expression; 
    var method = "OrderBy"; 
    foreach (var item in keySelectors) 
    { 
     var keySelector = item.Unwrap(); 
     result = Expression.Call(
      typeof(Queryable), method, new[] { typeof(T), keySelector.Body.Type }, 
      result, Expression.Quote(keySelector)); 
     method = "ThenBy"; 
    } 
    return (IOrderedQueryable<T>)source.Provider.CreateQuery(result); 
} 

Конечно, это не должно быть так. В вашем случае вы могли бы объединить два метода в одном (аналогично второму, но получая string sortedColumn), и в этом случае вы просто будете использовать не общий метод Expression.Lambda без переноса типов значений с помощью Convert.

+1

Это именно то, что мне нужно, идеально! Спасибо, Иван! Единственное различие заключается в том, что вместо использования методов расширения у меня просто был метод GetExpressions, возвращающий не-общую версию, как вы предполагали. Работает плавно! – JNYRanger