Linq Expression抛出InvalidOperationException

时间:2014-02-13 14:19:06

标签: .net linq nhibernate lambda linq-expressions

我正在构建一个Expression,它应该代表Equals属性和类型Nullable<long>的常量的比较。换句话说,编译表达式应返回类似于x => (x.Id == value)的lambda,其中Idvalue都是long?类型。

这是代码:

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的回答,我修复了代码。我假设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);
}

测试(仅适用于lambda编译版本):

[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 个答案:

答案 0 :(得分: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);
}