C# ExpressionTree方法将MemberInitExpression分配给属性

C# ExpressionTree方法将MemberInitExpression分配给属性,c#,entity-framework-6,expression-trees,C#,Entity Framework 6,Expression Trees,我正在尝试使用表达式树,这样我就可以选择使用实体框架映射到DTO,就像Include指令在DbSet(实现OData的OpenSorce项目的一部分)上工作一样 下面的代码表示一个测试用例 Expression<Func<Bar, Bar>> mapBar = b => new Bar { BarInt = b.BarInt, BarString = b.BarString }; Expression<Func<Foo, Foo>> mapF

我正在尝试使用表达式树,这样我就可以选择使用实体框架映射到DTO,就像Include指令在DbSet(实现OData的OpenSorce项目的一部分)上工作一样

下面的代码表示一个测试用例

Expression<Func<Bar, Bar>> mapBar = b => new Bar { BarInt = b.BarInt, BarString = b.BarString };
Expression<Func<Foo, Foo>> mapFoo = f => new Foo { FooInt = f.FooInt, B = null };

Expression<Func<Foo, Foo>> target = f => new Foo { FooInt = f.FooInt, 
    B = new Bar { 
        BarInt=f.B.BarInt, BarString = f.B.BarString 
    } };
但是我不知道如何将表达式
newbar{BarInt=b.BarInt,…
,改成
newbar{BarInt=f.b.BarInt,…

该函数将需要类似这样的ExpressionVisitor

public class MergingVisitor : ExpressionVisitor
{
    private readonly ParameterExpression _oldExpr;
    private readonly ParameterExpression _newProp;
    private readonly ParameterExpression _newParent;
    public MergingVisitor(ParameterExpression oldExpr, ParameterExpression newProp, ParameterExpression newParent)
    {
        _oldExpr = oldExpr;
        _newProp = newProp;
        _newParent = newParent;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression == _oldExpr)
        {
            /*!!what to do here!!*/
            var ma = Expression.MakeMemberAccess(_newProp, node.Member);
            return Expression.MakeMemberAccess(_newParent, ma.Member);
        }
        return base.VisitMember(node);
    }
它们都需要用类似的东西连接在一起

public static Expression<Func<T, TMap>> MapNavProperty<T, TMap, U, UMap>(this Expression<Func<T, TMap>> parent, Expression<Func<U, UMap>> nav, string propName)
{
    //concern 1 remap name of prop in nav - not sure if I should do this first
    var parentInitVarName = parent.Parameters[0].Name;
    var parentParam = Expression.Parameter(typeof(T), parentInitVarName);
    var propParam = Expression.Parameter(typeof(U), propName);
    var mergeVisitor = new MergingVisitor(nav.Parameters[0], propParam, parentParam);
    var newNavBody = mergeVisitor.Visit(nav.Body); //as MemberExpression;

    //concern 2 replace given property
    var visitor = new UpdateExpressionVisitor(propParam, nav.Body);

    return (Expression<Func<T, TMap>>)visitor.Visit(parent);
}
公共静态表达式MapNavProperty(此表达式父级、表达式nav、字符串propName)
{
//关注点1在导航中重新映射道具名称-不确定是否应首先执行此操作
var parentInitVarName=parent.Parameters[0].Name;
var parentParam=Expression.Parameter(typeof(T),parentInitVarName);
var propParam=Expression.Parameter(typeof(U),propName);
var mergeVisitor=new MergingVisitor(导航参数[0]、propParam、parentParam);
var newNavBody=mergeVisitor.Visit(nav.Body);//作为成员表达式;
//关注点2替换给定属性
var visitor=新的UpdateExpressionVisitor(propParam,nav.Body);
返回(表达)访客。访问(家长);
}
尽管目前parentParam在
表达式中会失败。MakeMemberAccess
函数的类型是T(上例中是Foo),而不是U

如何更改子属性的lamda表达式中的变量名和类型-谢谢

更新 Svick和Ivan Stoev的答案在解释上很博学,而且都很完美——两个答案中(通过的)单元测试和代码都是正确的。非常感谢你们两个——很遗憾我不能勾选两个答案,所以归结到Eeny,meeny,miny,moe

var parentParam = Expression.Parameter(typeof(T), parentInitVarName);
这不起作用,表达式中的参数不是通过名称标识的,而是通过引用标识的。您需要做的只是获取父级的参数


这对我来说没有任何意义。
propName
是属性的名称,因此需要使用它来创建成员访问表达式,而不是参数表达式


您需要的是将一个表达式(
b
)替换为另一个(
f.b
)。为此,我将创建:

public class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression _oldExpr;
    private readonly Expression _newExpr;

    public ReplaceVisitor(Expression oldExpr, Expression newExpr)
    {
        _oldExpr = oldExpr;
        _newExpr = newExpr;
    }

    public override Expression Visit(Expression node)
    {
        if (node == _oldExpr)
        {
            return _newExpr;
        }
        return base.Visit(node);
    }
}

oldExpr
作为参数表达式没有任何意义。您需要的只是一个
字符串


您需要在此处实际使用
newNavBody


整个
MapNavProperty()
现在将如下所示:

var parentParam = parent.Parameters.Single();
var propExpression = Expression.Property(parentParam, propName);
var mergeVisitor = new ReplaceVisitor(nav.Parameters.Single(), propExpression);
var newNavBody = mergeVisitor.Visit(nav.Body);

var visitor = new UpdateExpressionVisitor(propName, newNavBody);

return (Expression<Func<T, TMap>>)visitor.Visit(parent);
public static Expression<Func<T, TMap>> MapNavProperty<T, TMap, U, UMap>(this Expression<Func<T, TMap>> parent, Expression<Func<U, UMap>> nav, string propName)
{
    var parameter = parent.Parameters[0];
    var body = parent.Body.ReplaceMemberAssignment(
        typeof(TMap).GetProperty(propName),
        nav.ReplaceParameter(Expression.Property(parameter, propName))
    );
    return Expression.Lambda<Func<T, TMap>>(body, parameter);
}
var parentParam=parent.Parameters.Single();
var propExpression=Expression.Property(parentParam,propName);
var mergeVisitor=new ReplaceVisitor(nav.Parameters.Single(),propExpression);
var newNavBody=mergeVisitor.Visit(nav.Body);
var visitor=newupdateexpressionvisitor(propName,newNavBody);
返回(表达)访客。访问(家长);


通过这些更改,您的代码将正常工作。

我个人会使用不同的帮助程序:

(1) 用于替换参数

static Expression ReplaceParameter(this LambdaExpression lambda, Expression target)
{
    return lambda.Body.ReplaceParameter(lambda.Parameters.Single(), target);
}

static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
    return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}

class ParameterReplacer : ExpressionVisitor
{
    public ParameterExpression Source;
    public Expression Target;
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node == Source ? Target : base.VisitParameter(node);
    }
}
(2) 用于替换成员分配值

static Expression ReplaceMemberAssignment(this Expression expression, MemberInfo member, Expression value)
{
    return new MemberAssignmentReplacer { Member = member, Value = value }.Visit(expression);
}

class MemberAssignmentReplacer : ExpressionVisitor
{
    public MemberInfo Member;
    public Expression Value;
    protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
    {
        return node.Member == Member ? node.Update(Value) : base.VisitMemberAssignment(node);
    }
}
有了这些助手,所讨论的功能如下:

var parentParam = parent.Parameters.Single();
var propExpression = Expression.Property(parentParam, propName);
var mergeVisitor = new ReplaceVisitor(nav.Parameters.Single(), propExpression);
var newNavBody = mergeVisitor.Visit(nav.Body);

var visitor = new UpdateExpressionVisitor(propName, newNavBody);

return (Expression<Func<T, TMap>>)visitor.Visit(parent);
public static Expression<Func<T, TMap>> MapNavProperty<T, TMap, U, UMap>(this Expression<Func<T, TMap>> parent, Expression<Func<U, UMap>> nav, string propName)
{
    var parameter = parent.Parameters[0];
    var body = parent.Body.ReplaceMemberAssignment(
        typeof(TMap).GetProperty(propName),
        nav.ReplaceParameter(Expression.Property(parameter, propName))
    );
    return Expression.Lambda<Func<T, TMap>>(body, parameter);
}
公共静态表达式MapNavProperty(此表达式父级、表达式nav、字符串propName)
{
var参数=父参数[0];
var body=parent.body.ReplaceMemberAssignment(
typeof(TMap).GetProperty(propName),
ReplaceParameter(Expression.Property(参数,propName))
);
返回表达式.Lambda(主体,参数);
}
static Expression ReplaceParameter(this LambdaExpression lambda, Expression target)
{
    return lambda.Body.ReplaceParameter(lambda.Parameters.Single(), target);
}

static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
    return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}

class ParameterReplacer : ExpressionVisitor
{
    public ParameterExpression Source;
    public Expression Target;
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node == Source ? Target : base.VisitParameter(node);
    }
}
static Expression ReplaceMemberAssignment(this Expression expression, MemberInfo member, Expression value)
{
    return new MemberAssignmentReplacer { Member = member, Value = value }.Visit(expression);
}

class MemberAssignmentReplacer : ExpressionVisitor
{
    public MemberInfo Member;
    public Expression Value;
    protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
    {
        return node.Member == Member ? node.Update(Value) : base.VisitMemberAssignment(node);
    }
}
public static Expression<Func<T, TMap>> MapNavProperty<T, TMap, U, UMap>(this Expression<Func<T, TMap>> parent, Expression<Func<U, UMap>> nav, string propName)
{
    var parameter = parent.Parameters[0];
    var body = parent.Body.ReplaceMemberAssignment(
        typeof(TMap).GetProperty(propName),
        nav.ReplaceParameter(Expression.Property(parameter, propName))
    );
    return Expression.Lambda<Func<T, TMap>>(body, parameter);
}