将LINQ表达式谓词从一种类型更改为另一种类型 [英] Change a LINQ expression predicate from one type to another

查看:56
本文介绍了将LINQ表达式谓词从一种类型更改为另一种类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个不相关的课程.一个公开为API,另一个公开为第三方API内部使用.

I have two unrelated classes. One is exposed as API, and the other is used internally by 3rd party API.

实体是通过我们的API公开的,而EntityProvider是来自第3方程序集的.

Entity is exposed from our API, while EntityProvider is from the 3rd party assembly.

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

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

我们的API的消费者将提供形式为Expression <Func<Entity, bool>>的谓词,我需要将其修改为Expression <Func<EntityProvider, bool>>,以便可以将其传递给内部第三方程序集. 请协助进行此转换.

Consumers of our API will provide predicates of the form Expression <Func<Entity, bool>> and I need to modify it to Expression <Func<EntityProvider, bool>> so that I can pass the same to internal 3rd party assembly. Please help with this conversion.

推荐答案

由于.NET中的表达式是不可变的,因此唯一的方法是重建整个表达式.为此,通常需要从 ExpressionVisitor 类.根据表达式的复杂性,您必须进行转换,这可能会非常复杂.

Since Expressions in .NET are immutable, the only way to do this is to rebuild the whole expression. To do this usually involves inheriting from the ExpressionVisitor class. Depending on the complexity of the expressions you have to convert this could be quite complicated.

这是一个访问者的简单示例,它将使用简单的表达式(例如x => x.Someproperty == somevalue).仅仅是一个入门的例子,它绝没有完成或经过测试(例如,它不会处理表达式中的方法调用)

This is a simple example of a visitor that will work with simple expressions( like x=>x.Someproperty == somevalue ). It's just an example to get you started and it's in no way finished or tested(it won't handle method calls in the expression for example)

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

这篇关于将LINQ表达式谓词从一种类型更改为另一种类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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