2014-02-13 3 views
1

Я строию Expression, который должен представлять собой сравнение свойств и констант Nullable<long>. Другими словами, компиляция выражения должна возвращать лямбда, подобную x => (x.Id == value), где оба Id и value имеют тип long?.Linq Expression throws InvalidOperationException

Это код:

private static Expression<Func<T, bool>> GetNullableIdEqualsQuery(long? value) 
{ 
    var type = typeof(T); 

    var idProperty = type.GetProperty("Id"); 
    var xParam = Expression.Parameter(type, "x"); 

    var block = Expression.Block(
     typeof(bool), 
     Expression.Equal(
      Expression.Property(xParam, idProperty), 
      Expression.Constant(value, typeof(long?))) 
     ); 

    return Expression.Lambda<Func<T, bool>>(block, xParam); 
} 

Но при использовании в запросе, он не с InvalidOperationException:

System.InvalidOperationException: переменная 'x' типа 'SomeEntity' ссылочного из сферы '', но он не определен.

Что я делаю неправильно?

[Редактировать]

Благодаря @ MarcGravell Ответим, я исправил код. Я предполагаю, что что-то было нарушено в LINQ-провайдере NHibernate, но сейчас у меня нет времени для дальнейшего расследования.

Если кому-то нужен универсальный вариант, который будет (ну, должен) работать на любой тип недвижимости, здесь:

public static Expression<Func<Tobj, bool>> GetEqualsQuery<Tobj, Tprop>(Tprop value, string propertyName) 
{ 
    var type = typeof(Tobj); 
    var property = type.GetProperty(propertyName); 
    var propertyType = property.PropertyType; 
    if (propertyType != typeof(Tprop)) 
     throw new InvalidOperationException("Property type ({0}) does not match the value type ({1})" 
      .FormatWith(propertyType, typeof(Tprop))); 

    var xParam = Expression.Parameter(type, "x"); 

    var body = Expression.Equal(
     Expression.Property(xParam, property), 
     Expression.Constant(value, propertyType) 
    ); 

    return Expression.Lambda<Func<Tobj, bool>>(body, xParam); 
} 

Test (для лямбды скомпилированных версий):

[TestClass] 
public class ExpressionHelperTest 
{ 
    class Test 
    { 
     public long Id { get; set; } 
    } 

    [TestMethod] 
    public void GetEqualsQueryWorksForSimpleTypes() 
    { 
     // create a query for the lambda x => x.Id == 5 
     var lambda = ExpressionHelper 
      .GetEqualsQuery<Test, long>(5, "Id") 
      .Compile(); 

     Assert.IsTrue(lambda(new Test() { Id = 5 })); 
     Assert.IsFalse(lambda(new Test() { Id = 8 })); 
    } 
} 
+1

Я думаю, что код в вашем примере работает нормально; возможно ли, что в коде * real * вы не подаете параметр в выражение (в вызове 'Lambda '), или, возможно, вы переписываете выражение без подстановки параметров? –

+0

@Marc: на самом деле, я передавал его NHibernate (не совсем последняя сборка), поэтому возможно, что проверка «null» не реализована должным образом в их поставщике (как вы упомянули ниже). – Groo

ответ

4

Только не используйте Expression.Block:

var body = Expression.Equal(
      Expression.Property(xParam, idProperty), 
      Expression.Constant(value, typeof(long?))); 

    return Expression.Lambda<Func<T, bool>>(body, xParam); 

Также обратите внимание, что некоторые провайдеры не правильно выполните вышеуказанное, когда значение равно null. Если вам нужно искать null матчей, возможно, потребуется специальный случай, что сценарий и явно проверить HasValue:

private static Expression<Func<T, bool>> GetNullableIdEqualsQuery<T>(long? value) 
{ 
    var xParam = Expression.Parameter(typeof(T), "x"); 
    Expression body; 
    if (value == null) 
    { 
     body = Expression.Not(
      Expression.Property(
       Expression.Property(xParam, "Id"), 
       "HasValue")); 
    } 
    else 
    { 
     body = Expression.Equal(
      Expression.Property(xParam, "Id"), 
      Expression.Constant(value, typeof(long?))); 
    } 
    return Expression.Lambda<Func<T, bool>>(body, xParam); 
} 
Смежные вопросы