将MemberInitExpression分配给属性的ExpressionTree方法 [英] ExpressionTree method to Assign MemberInitExpression to property

查看:94
本文介绍了将MemberInitExpression分配给属性的ExpressionTree方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用表达式树,以便我可以选择使用实体框架映射到DTO,方法与Include指令在DbSet上起作用的方式相同(属于实现OData的开放式源代码项目的一部分).

I am trying to use expression trees so that I can choose to map across to a DTO using entity framework in much the same way as the Include directive works on a DbSet (part of an open sorce project implementing OData).

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

The code below represents a test case.

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为空,并插入了mapBar表达式.

note Foo.B was null, and has the mapBar expression inserted.

我可以使用以下ExpressionVisitor添加导航属性

I have got as far as adding the navigation property using the following 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,...

But I cant figure out how to alter the expression new Bar { BarInt = b.BarInt,..., to become new Bar { BarInt = f.B.BarInt,...

该函数将需要像这样的ExpressionVisitor

The function will need some kind of ExpressionVisitor like so

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

它们都需要与类似的东西链接在一起

and they all need to be linked together with something like

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.

although at present the parentParam will fail in the Expression.MakeMemberAccess function as it is of type T (Foo in the above example), rather than type U.

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

How can I alter the variable name and types in the child property's lamda expression - thank you.

Svick和Ivan Stoev的答案在解释上很博学,并且都很好用-两个答案的(通过的)单元测试和代码都已建立

The answers from Svick and Ivan Stoev are erudite in explanation and both work perfectly - the (passing) unit tests and code from both answers are up on GitHub here. Thank you so much to both of you - it is a shame I cannot tick 2 answers, so it comes down to Eeny, meeny, miny, moe.

推荐答案

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

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

This won't work, parameters in expressions are not identified by name, but by reference. What you need to do is just get the parent's parameter.

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

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

This doesn't make any sense to me. propName is the name of a property, so you need to use it to create member access expression, not parameter expression.

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

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

What you need is to replace one expression (b) with another (f.B). For that, I would create:

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.

It doesn't make any sense for oldExpr to be a parameter expression. What you need is just a string.

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

您需要在此处实际使用newNavBody.

You need to actually use newNavBody here.

整个MapNavProperty()现在看起来像这样:

The whole MapNavProperty() will now look like this:

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


通过这些更改,您的代码将可用.


With these changes, your code will work.

这篇关于将MemberInitExpression分配给属性的ExpressionTree方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆