2015-02-12 6 views
0

Я имею дело с scanario типов Nullable во время формирования динамических выражений запроса. Эти выражения будут извлекать отфильтрованные данные из любых таблиц SQL (взаимодействующих с классами First Code с использованием EF).Как получить выражение для Nullable значений (полей) без преобразования из Expression.Convert в C#?

У меня нормальный объект (например, Консигнация работает с несколькими свойствами наряду с свойствами Nullable).

Мое формирование выражения идет хорошо до тех пор, пока я не встречу некоторые типы Nullable. На этих nullables, я получаю

Бинарный оператор NotEqual не определен для 'System.Nullable`1 [System.Single]' типов и 'System.Single'.

Для устранения этого исключения, я использую все оценки относительно конвертации, размещенные на разных потоках.

Invoking lambda expressions in Expression trees

Trying to filter on a Nullable type using Expression Trees

Это все порождающих выражения с добавлением слова "Преобразовать" (то есть конвертировать (SomeValue)), и в результате у меня всегда есть выражение

t=>(t.Consignment.Id = 45000 && t.Consignment.someProperty>=45 Or t.Consignment.Weight! = Convert(5000)). 

Конечно, мне нужно все выше выражение БЕЗ "Конвертировать". Поскольку этот «Конвертировать» не будет получать данные из таблиц соответственно.

Любая помощь была бы принята с благодарностью! Что нужно делать? Я уже знаю, преобразование, но это делает все выражение бесполезно, потому что он не будет проецировать записи из-за ненужной «Преобразование»

Добавлено

Expression NotEqual<T>(Expression PropertyType, ConstantExpression a_Constant, ParameterExpression parameter) 
    { 
    if(IsNullableType(Property.Type) &&!IsNullableType(a_Constant.Type)) 
    { 
     var converted = a_Constant.Type != Property.Type ? (Expression)Expression.Convert(a_Constant, Property.Type): (Expression)a_Constant; 

    // here above statement returns (Convert(50000)) and all I want (50000), but i tried all combinitions from Expression in order to form this constant as expression, it always throws exception what I mentioned originally. 

    var body = Expression.MakeBinary(ExpressionType.NotEqual, PropertyType, converted); 

    //MakeBinary statement returns {(t.Weight != Convert(5000000))} but I need {(t.Weight != 5000000)} 

    var expr = Expression.Lambda<Func<T, bool>>(body, parameter); 
    return expr; 
    } 
    } 

Код:

public class Consignment 
{ 
    public float? Weight { get; set; } 
}  

public static class GenericQueryExpressionBuilder 
{   
    private static Expression NotEqual<T>(Expression memberExpression, ConstantExpression a_Constant, ParameterExpression parameter) 
    { 
     ConstantExpression constantExpression = null; 

     if (IsNullableType(memberExpression.Type) && !IsNullableType(a_Constant.Type)) 
     {     
      //var converted = a_Constant.Type != memberExpression.Type ? (Expression)Expression.Convert(a_Constant, memberExpression.Type) : (Expression)a_Constant; 

      Expression constantExp = Expression.Property(a_Constant,typeof(T),"Weight"); 

     **// above statement throws exception I commented.** 

      var body = Expression.MakeBinary(ExpressionType.NotEqual, memberExpression, converted); 

      //here I want "t=>(t.Weight!=5000.0) INSTEAD of t=>(t.Weight!=Convert(5000.0))" 

      var expr = Expression.Lambda<Func<T, bool>>(body, parameter); 
      return expr; 
     } 

     else if (!IsNullableType(memberExpression.Type) && IsNullableType(a_Constant.Type)) 
      memberExpression = Expression.Convert(memberExpression, a_Constant.Type); 

     return Expression.NotEqual(memberExpression, constantExpression); 
    } 

    static bool IsNullableType(Type t) 
    { 
     return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); 
    }   

    private static Expression GetExpression<T>(ParameterExpression param, string a_strPropertyName, string Operator, object Value) 
    {    
     MemberExpression member = Expression.Property(param, a_strPropertyName); 
     ConstantExpression constant = Expression.Constant(Value); 

     try 
     {    
      return GenericQueryExpressionBuilder.NotEqual<T>(member, constant, param);    

     } 
     catch (InvalidOperationException) 
     { 
      return null; 
     } 

     return null; 
    } 

    public static Expression<Func<T, bool>> GetExpression<T>(Consignment consignment) 
    { 
     Expression expression = null; 

     var parameter = Expression.Parameter(typeof(T), "t"); 

     string PropertyName = "Weight"; 
     string Operation = "NotEqual"; 
     object Value = consignment.Weight; 

     expression = GenericQueryExpressionBuilder.GetExpression<T>(parameter, PropertyName, Operation, Value); 
     return Expression.Lambda<Func<T, bool>>(expression, parameter); 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 


     Consignment consignment = new Consignment(); 
     consignment.Weight = 50000.0f; 

     var deleg = GenericQueryExpressionBuilder.GetExpression<Consignment>(consignment).Compile();    

    } 
    } 
+0

Что вы хотите, если значение равно null? И что * точно * вы подразумеваете под «не будет получать данные из таблиц»? Что представляет собой сгенерированный SQL, и для чего вам это нужно? –

+0

Jon Skeet: Конечно, если значение равно null, то это будет просто пропущено. У меня есть пользовательский интерфейс, где пользователи вводят свои фильтры вручную. Эти фильтры затем преобразуются для формирования выражений. Затем эти выражения применяются к огромному набору данных для получения фильтрованных записей. Итак, в моем случае ther не будет фильтром, который вообще может содержать обнуляемый vlaue. Но таблица данных, отображаемая с помощью кода, сначала работающего с полями с нулевым значением – Usman

+0

Я ничего не вижу «конечно» об этом ... существуют определенные варианты, которые могут быть одинаково действительными. Но похоже, что вы хотите »(t.Consignment.Weight.HasValue && t.Consignment.Weight.Value! = 5000)' или что-то в этом роде. Другими словами, вы можете использовать свойства «HasValue» и «Value» для укажите * точно *, что вы хотите. –

ответ

7

Ниже приведен короткий, но полный пример, показывающий, как построить дерево выражений . Я удалил много нерелевантного кода из вопроса:

using System; 
using System.Linq.Expressions; 

public class Consignment 
{ 
    public float? Weight { get; set; } 
}  

public class Test 
{   
    private static Expression NotEqual(Expression memberExpression, 
             ConstantExpression constantToCompare) 
    { 
     // Other cases removed, for simplicity. This answer only demonstrates 
     // how to handle c => c.Weight != 5000f. 
     var hasValueExpression = Expression.Property(memberExpression, "HasValue"); 
     var valueExpression = Expression.Property(memberExpression, "Value"); 
     var notEqual = Expression.NotEqual(valueExpression, constantToCompare); 
     return Expression.AndAlso(hasValueExpression, notEqual); 
    } 

    static void Main(string[] args) 
    { 
     Consignment consignment = new Consignment(); 
     consignment.Weight = 50000.0f; 

     var parameter = Expression.Parameter(typeof(Consignment), "c"); 
     var weight = Expression.Property(parameter, "Weight"); 
     var constant = Expression.Constant(5000f, typeof(float)); 
     var weightNotEqualExpression = NotEqual(weight, constant); 
     var lambda = Expression.Lambda<Func<Consignment, bool>> 
      (weightNotEqualExpression, parameter); 
     Console.WriteLine(lambda); 
    } 
} 
+0

Спасибо Jon Skeet: Он работал так, как я ожидал! Я должен был сделать некоторую жесткую кодировку, чтобы сообщить точный тип Expression.Constant (ExpressionValie, tyepof (....)). Но прежде, чем мне пришлось применить все проверки сборки в типах (float, string, int ....). Спасибо за ваше время и все, что вы сделали! – Usman

+0

@Usman: Если у вас есть значение (возможно, в коробке), вы можете просто называть 'GetType()' на этом.Или сделайте его общим в постоянном типе и используйте 'typeof (T)'. –