MemberExpression,从 class 构建 Expression.Property
MemberExpression, build Expression.Property from class
下面的表达式将 属性 NAME 与值 PETER 进行比较。
ParameterExpression pe = Expression.Parameter(typeof(T), "x");
MemberExpression member = Expression.Property(pe, "name");
ConstantExpression value = Expression.Constant("Peter");
exp = Expression.Equal(member, value);
如果 属性 是 class 怎么办:
public class Address
{
public string Name {get; set;}
}
那么表达式看起来类似于:
MemberExpression member = Expression.Property(pe, "Address.Name");
ConstantExpression value = Expression.Constant("Peter");
exp = Expression.Equal(member, value);
这会失败,因为成员类型与值类型不匹配。
所以,问题是:如何使用上面的 class 示例构建一个可以工作的表达式??
我在 NHibernate.Linq 查询中使用这个表达式:
var q = from f in data //of type IQueryable<T>
select f;
if (filter != null) //filter of type Expression<Func<T, bool>>
q = q.Where(filter);
etc....
谢谢。
彼得更新:
基于 xanatos 的代码(下一个 post),我创建了以下测试以了解其工作原理。它与 xanatos 所做的并没有太大不同,但起初我无法让它工作,所以我决定在一个简单的测试中将它全部编写,然后就成功了。感谢 xanatos:
[Test]
public void FilterWithDeepProperties()
{
//Arrange
IGenericGridRepository repository = ObjectFactory.GetInstance<IGenericGridRepository>();
FilterDescriptor filter = new FilterDescriptor("AgreementId.Name", FilterOperator.IsEqualTo, "a name");
string[] properties = filter.Member.Split('.');
ParameterExpression pe = Expression.Parameter(typeof(SampleDomain), "x");
//Act
Expression lastMember = pe;
for (int i = 0; i < properties.Length; i++)
{
MemberExpression member = Expression.Property(lastMember, properties[i]);
lastMember = member;
}
ConstantExpression valueExpression = Expression.Constant(filter.Value);
Expression equalityExpression = Expression.Equal(lastMember, valueExpression);
Expression<Func<SampleDomain, bool>> where = Expression.Lambda<Func<SampleDomain, bool>>(equalityExpression, pe);
var result = repository.GetObjects<SampleDomain>(filter: where);
//Assert
result.Count().Should().BeGreaterThan(0, "because there are many schedule items equals to " + filter.Value);
}
你可能想要这样的东西:
public static Expression<Func<TSource, bool>> GetEquality<TSource>(object value, params string[] properties)
{
ParameterExpression pe = Expression.Parameter(typeof(TSource), "source");
Expression lastMember = pe;
for (int i = 0; i < properties.Length; i++)
{
MemberExpression member = Expression.Property(lastMember, properties[i]);
lastMember = member;
}
Expression valueExpression = Expression.Constant(value);
Expression equalityExpression = Expression.Equal(lastMember, valueExpression);
Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(equalityExpression, pe);
return lambda;
}
像这样使用它:
Expression exp = GetEquality<Person>("Foo", "Address", "Name");
其中 Foo
是您的 Peter
(因此必须比较的值),而 Address
和 Name
是 "chain" 的名称属性。例如我正在使用
public class Person
{
public Address Address { get; set; }
}
public class Address
{
public string Name { get; set; }
}
所以生成的表达式是
source.Address.Name == "Foo"
如果你想使用Address.Name
,你可以使用
这样的方法
Expression exp = GetEquality<Person>("Foo", "Address.Name".Split('.'));
下面的表达式将 属性 NAME 与值 PETER 进行比较。
ParameterExpression pe = Expression.Parameter(typeof(T), "x");
MemberExpression member = Expression.Property(pe, "name");
ConstantExpression value = Expression.Constant("Peter");
exp = Expression.Equal(member, value);
如果 属性 是 class 怎么办:
public class Address
{
public string Name {get; set;}
}
那么表达式看起来类似于:
MemberExpression member = Expression.Property(pe, "Address.Name");
ConstantExpression value = Expression.Constant("Peter");
exp = Expression.Equal(member, value);
这会失败,因为成员类型与值类型不匹配。
所以,问题是:如何使用上面的 class 示例构建一个可以工作的表达式??
我在 NHibernate.Linq 查询中使用这个表达式:
var q = from f in data //of type IQueryable<T>
select f;
if (filter != null) //filter of type Expression<Func<T, bool>>
q = q.Where(filter);
etc....
谢谢。
彼得更新:
基于 xanatos 的代码(下一个 post),我创建了以下测试以了解其工作原理。它与 xanatos 所做的并没有太大不同,但起初我无法让它工作,所以我决定在一个简单的测试中将它全部编写,然后就成功了。感谢 xanatos:
[Test]
public void FilterWithDeepProperties()
{
//Arrange
IGenericGridRepository repository = ObjectFactory.GetInstance<IGenericGridRepository>();
FilterDescriptor filter = new FilterDescriptor("AgreementId.Name", FilterOperator.IsEqualTo, "a name");
string[] properties = filter.Member.Split('.');
ParameterExpression pe = Expression.Parameter(typeof(SampleDomain), "x");
//Act
Expression lastMember = pe;
for (int i = 0; i < properties.Length; i++)
{
MemberExpression member = Expression.Property(lastMember, properties[i]);
lastMember = member;
}
ConstantExpression valueExpression = Expression.Constant(filter.Value);
Expression equalityExpression = Expression.Equal(lastMember, valueExpression);
Expression<Func<SampleDomain, bool>> where = Expression.Lambda<Func<SampleDomain, bool>>(equalityExpression, pe);
var result = repository.GetObjects<SampleDomain>(filter: where);
//Assert
result.Count().Should().BeGreaterThan(0, "because there are many schedule items equals to " + filter.Value);
}
你可能想要这样的东西:
public static Expression<Func<TSource, bool>> GetEquality<TSource>(object value, params string[] properties)
{
ParameterExpression pe = Expression.Parameter(typeof(TSource), "source");
Expression lastMember = pe;
for (int i = 0; i < properties.Length; i++)
{
MemberExpression member = Expression.Property(lastMember, properties[i]);
lastMember = member;
}
Expression valueExpression = Expression.Constant(value);
Expression equalityExpression = Expression.Equal(lastMember, valueExpression);
Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(equalityExpression, pe);
return lambda;
}
像这样使用它:
Expression exp = GetEquality<Person>("Foo", "Address", "Name");
其中 Foo
是您的 Peter
(因此必须比较的值),而 Address
和 Name
是 "chain" 的名称属性。例如我正在使用
public class Person
{
public Address Address { get; set; }
}
public class Address
{
public string Name { get; set; }
}
所以生成的表达式是
source.Address.Name == "Foo"
如果你想使用Address.Name
,你可以使用
Expression exp = GetEquality<Person>("Foo", "Address.Name".Split('.'));