2014-02-13 63 views
1

我正在构建一个Expression应该代表一个Equals比较属性和Nullable<long>类型的常量。换句话说,编译表达式应该返回类似于x => (x.Id == value)的lambda,其中Idvalue的类型都是long?Linq表达式抛出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:从范围'''SomeEntity'引用类型的可变'x',但它没有被定义。

我在做什么错?

[编辑]

感谢@ MarcGravell的回答,我已经固定的代码。我假设NHibernate的LINQ提供商中有些东西被破坏了,但现在我没有时间进一步调查了。

如果有人需要一个通用版本,这将(当然,应该)取得任何财产类型的工作,那就是:

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); 
} 

测试(用于拉姆达编译版本):

[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); 
} 
相关问题