2014-02-13 60 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); 
} 
相關問題