我有这样的方法:
private bool Method_1(Expression<Func<IPerson, bool>> expression)
{
/* Some code that will call Method_2 */
}
在这种方法中,我想将IPerson
类型更改为其他类型。我想调用另一个看起来像这样的方法:
private bool Method_2(Expression<Func<PersonData, bool>> expression)
{
/* Some code */
}
因此,在method_1
我需要将IPerson
更改为PersonData
。我怎么能这样做?
编辑:
当我致电:Method_1(p => p.Id == 1)
我想'保存'条件(p.Id == 1
),但我想在另一种类型上执行此条件,即IPerson
。因此,我需要更改表达式或使用IPerson
编辑II:
对于那些感兴趣的人,这是(现在)我的解决方案:
private class CustomExpressionVisitor<T> : ExpressionVisitor
{
ParameterExpression _parameter;
public CustomExpressionVisitor(ParameterExpression parameter)
{
_parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.MemberType == System.Reflection.MemberTypes.Property)
{
MemberExpression memberExpression = null;
var memberName = node.Member.Name;
var otherMember = typeof(T).GetProperty(memberName);
memberExpression = Expression.Property(Visit(node.Expression), otherMember);
return memberExpression;
}
else
{
return base.VisitMember(node);
}
}
}
这就是我使用它的方式:
public virtual bool Exists(Expression<Func<Dto, bool>> expression)
{
var param = Expression.Parameter(typeof(I));
var result = new CustomExpressionVisitor<I>(param).Visit(expression.Body);
Expression<Func<I, bool>> lambda = Expression.Lambda<Func<I, bool>>(result, param);
bool exists = _repository.Exists(lambda);
return exists;
}
答案 0 :(得分:17)
如果您使用.net 4(更新:如评论ExpressionVisitor
中所述,在版本4而不是4.5中添加),这很容易。它需要对旧框架进行一些Google搜索:
有一些假设,但我认为它们对您的DTO和实体场景有效 - 访问的属性必须匹配。
class PersonData
{
public bool Prop { get; set; }
}
interface IPerson
{
bool Prop { get; set; }
}
在.net 4中定义了ExpressionVisitor
类,如果您使用较旧的类,则需要编写或查找它的实现,这样会更容易:
class Visitor<T> : ExpressionVisitor
{
ParameterExpression _parameter;
//there must be only one instance of parameter expression for each parameter
//there is one so one passed here
public Visitor(ParameterExpression parameter)
{
_parameter = parameter;
}
//this method replaces original parameter with given in constructor
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
//this one is required because PersonData does not implement IPerson and it finds
//property in PersonData with the same name as the one referenced in expression
//and declared on IPerson
protected override Expression VisitMember(MemberExpression node)
{
//only properties are allowed if you use fields then you need to extend
// this method to handle them
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
throw new NotImplementedException();
//name of a member referenced in original expression in your
//sample Id in mine Prop
var memberName = node.Member.Name;
//find property on type T (=PersonData) by name
var otherMember = typeof(T).GetProperty(memberName);
//visit left side of this expression p.Id this would be p
var inner = Visit(node.Expression);
return Expression.Property(inner, otherMember);
}
}
概念证明:
class Program
{
static void Main()
{
//sample expression
Expression<Func<IPerson, bool>> expression = x => x.Prop;
//parameter that will be used in generated expression
var param = Expression.Parameter(typeof(PersonData));
//visiting body of original expression that gives us body of the new expression
var body = new Visitor<PersonData>(param).Visit(expression.Body);
//generating lambda expression form body and parameter
//notice that this is what you need to invoke the Method_2
Expression<Func<PersonData, bool>> lambda = Expression.Lambda<Func<PersonData, bool>>(body, param);
//compilation and execution of generated method just to prove that it works
var boolValue = lambda.Compile()(new PersonData());
}
}
请注意,这适用于简单表达式。如果您需要处理x.Prop.Prop1 < 3
,则需要进一步扩展。