3

私は2つの無関係なクラスを持っています。1 つは API として公開され、もう 1 つはサードパーティ API によって内部的に使用されます。

Entity は API から公開されますが、EntityProvider はサードパーティのアセンブリから公開されます。

class Entity
{
  public A { get; set; }
}

class EntityProvider
{
  public A { get; set; }
}

私たちの API のコンシューマはフォームの述語を提供します。内部のサード パーティ アセンブリに同じものを渡すことができるように、Expression <Func<Entity, bool>>それを変更する必要があります。Expression <Func<EntityProvider, bool>>この変換を手伝ってください。

4

1 に答える 1

5

.NETの式は不変であるため、これを行う唯一の方法は、式全体を再構築することです。これを行うには、通常、ExpressionVisitorクラスから継承する必要があります。変換する必要のある式の複雑さによっては、これは非常に複雑になる可能性があります。

これは、単純な式(x => x.Someproperty == somevalueなど)を使用する訪問者の単純な例です。これは開始するための単なる例であり、終了またはテストされているわけではありません(たとえば、式のメソッド呼び出しは処理されません)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
//Type from which to convert
public class A
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
}

//Type to which we want the Expression converted
public class B
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        //the expression we want to convert expresion
        Expression<Func<A, bool>> expA = x => x.Property1 == 6 && x.Property2 == 3;

        var visitor = new ParameterTypeVisitor<A,B>(expA);
        var expB = visitor.Convert();
        var b = new B() { Property1 = 6, Property2 = 3 };

        //try the converted expression
        var result = expB.Compile().Invoke(b);

    }
}    

public class ParameterTypeVisitor<TFrom,TTo> : ExpressionVisitor
{

    private Dictionary<string, ParameterExpression> convertedParameters;
    private Expression<Func<TFrom, bool>> expression;

    public ParameterTypeVisitor(Expression<Func<TFrom,bool>> expresionToConvert )
    {
        //for each parameter in the original expression creates a new parameter with the same name but with changed type 
        convertedParameters = expresionToConvert.Parameters
            .ToDictionary(
                x => x.Name,
                x => Expression.Parameter(typeof (TTo), x.Name)
            );

        expression = expresionToConvert;
    }

    public Expression<Func<TTo,bool>> Convert()
    {
        return (Expression<Func<TTo, bool>>)Visit(expression);
    }

    //handles Properties and Fields accessors 
    protected override Expression VisitMember(MemberExpression node)
    {
        //we want to replace only the nodes of type TFrom
        //so we can handle expressions of the form x=> x.Property.SubProperty
        //in the expression x=> x.Property1 == 6 && x.Property2 == 3
        //this replaces         ^^^^^^^^^^^         ^^^^^^^^^^^            
        if (node.Member.DeclaringType == typeof(TFrom))
        {
            //gets the memberinfo from type TTo that matches the member of type TFrom
            var memeberInfo = typeof (TTo).GetMember(node.Member.Name).First();

            //this will actually call the VisitParameter method in this class
            var newExp = Visit(node.Expression);
            return Expression.MakeMemberAccess(newExp, memeberInfo);
        }
        else 
        {
            return base.VisitMember(node);
        }
    }

    // this will be called where ever we have a reference to a parameter in the expression
    // for ex. in the expression x=> x.Property1 == 6 && x.Property2 == 3
    // this will be called twice     ^                   ^
    protected override Expression VisitParameter(ParameterExpression node)
    {            
        var newParameter = convertedParameters[node.Name];
        return newParameter;
    }

    //this will be the first Visit method to be called
    //since we're converting LamdaExpressions
    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        //visit the body of the lambda, this will Traverse the ExpressionTree 
        //and recursively replace parts of the expression we for which we have matching Visit methods 
        var newExp = Visit(node.Body);

        //this will create the new expression            
        return Expression.Lambda(newExp,convertedParameters.Select(x=>x.Value));
    }        
}
于 2013-01-21T12:52:51.213 に答える