将MemberInitExpression分配给属性的ExpressionTree方法

布伦特

我正在尝试使用表达式树,以便可以选择使用实体框架映射到DTO,方法与Include指令在DbSet上(在实施OData的开放式开源项目的一部分)上使用的方式几乎相同。

下面的代码代表一个测试案例。

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 
    } };

注意Foo.B为null,并插入了mapBar表达式。

我已经使用以下ExpressionVisitor添加了导航属性

public class UpdateExpressionVisitor : ExpressionVisitor
{
    private readonly ParameterExpression _oldExpr;
    private readonly Expression _newExpr;
    public UpdateExpressionVisitor(ParameterExpression oldExpr, Expression newExpr)
    {
        _oldExpr = oldExpr;
        _newExpr = newExpr;
    }

    protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
    {
        if (node.Member.Name == _oldExpr.Name)
        {
            return node.Update(_newExpr);
        }
        Console.WriteLine(node);
        return base.VisitMemberAssignment(node);
    }
}

但我不知道如何改变表达方式new Bar { BarInt = b.BarInt,...,成为new Bar { 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);
}

尽管目前parentParam将失败,Expression.MakeMemberAccess因为它的类型为T(在上面的示例中为Foo),而不是类型U。

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

更新

Svick和Ivan Stoev的答案在解释上是博学的,并且都可以完美地工作-两种测试的(通过的)单元测试和代码都在GitHub上发布非常感谢你们两个-真可惜,我无法在2个答案上打勾,因此归结为Eeny,meeny,miny,moe。

斯威克
var parentParam = Expression.Parameter(typeof(T), parentInitVarName);

这将不起作用,表达式中的参数不是通过名称标识的,而是通过引用标识的。您需要做的只是获取父级的参数。


var propParam = Expression.Parameter(typeof(U), propName);

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


public MergingVisitor(ParameterExpression oldExpr, ParameterExpression newProp, ParameterExpression newParent)

您需要将一个表达式(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);
    }
}

public UpdateExpressionVisitor(ParameterExpression oldExpr, Expression newExpr)

oldExpr成为参数表达式没有任何意义您所需要的只是一个string


var visitor = new UpdateExpressionVisitor(propParam, nav.Body);

您需要在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);

通过这些更改,您的代码将可以使用。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

将MemberInitExpression分配给属性的ExpressionTree方法

来自分类Dev

将MemberInitExpression分配给属性的ExpressionTree方法

来自分类Dev

操纵对象的属性:将方法分配给属性

来自分类Dev

将方法分配给变量/属性的语法

来自分类Dev

如何将类方法分配给类属性?

来自分类Dev

将属性分配给功能

来自分类Dev

将值分配给Swift属性

来自分类Dev

将NSNotFound分配给属性?

来自分类Dev

将属性分配给功能

来自分类Dev

将范围分配给类属性

来自分类Dev

Rails将属性分配给实例

来自分类Dev

将Swift方法分配给变量

来自分类Dev

样式属性将覆盖分配给jquery的css属性

来自分类Dev

将模型方法的返回值分配给模型属性

来自分类Dev

无法将方法组分配给匿名类型属性 MVC c# Visual Studios

来自分类Dev

分配给属性无效

来自分类Dev

将Json对象属性分配给模型

来自分类Dev

使用装饰器将属性分配给非原型

来自分类Dev

将属性分配给JavaScript中的函数引用

来自分类Dev

如何将枚举分配给viewmodel属性?

来自分类Dev

将值分配给string()类型的自动实现的属性

来自分类Dev

使用C ++将属性分配给任意R对象?

来自分类Dev

将子类分配给父类类型的属性

来自分类Dev

将属性分配给多对多关系

来自分类Dev

将通用接口的实现分配给属性

来自分类Dev

将保留对象分配给弱属性

来自分类Dev

自动将值分配给$ fillable属性(Laravel 4)

来自分类Dev

将绑定分配给附加属性以隐藏GridViewColumn

来自分类Dev

是否可以将CSS分配给CSS的content属性?