繁体   English   中英

如何映射表达式 <Func<TEntity, bool> &gt;表达 <Func<TDbEntity, bool> &gt;

[英]How to map Expression<Func<TEntity, bool>> to Expression<Func<TDbEntity, bool>>

我该如何映射?

from: Expression<Func<TEntity, bool>> to: Expression<Func<TDbEntity, bool>>

where TEntity: class, new() and TDbEntity: class, new()

TEntity来自Domain,TDbEntity来自Infrastructure层,但具有相同的属性。

有可能的?

对于相对简单的情况(我想在你的情况下表达式很简单)你可以使用ExpressionVisitor只有几个覆盖。 例如:

public static class ExpressionExtensions {
    public static Expression<Func<TTo, bool>> ReplaceParameter<TFrom, TTo>(this Expression<Func<TFrom, bool>> target) {
        return (Expression<Func<TTo, bool>>) new WhereReplacerVisitor<TFrom, TTo>().Visit(target);
    }
    private class WhereReplacerVisitor<TFrom, TTo> : ExpressionVisitor {
        private readonly ParameterExpression _parameter = Expression.Parameter(typeof(TTo), "c");

        protected override Expression VisitLambda<T>(Expression<T> node) {
            // replace parameter here
            return Expression.Lambda(Visit(node.Body), _parameter);
        }            

        protected override Expression VisitMember(MemberExpression node) {
            // replace parameter member access with new type
            if (node.Member.DeclaringType == typeof(TFrom) && node.Expression is ParameterExpression) {
                return Expression.PropertyOrField(_parameter, node.Member.Name);
            }
            return base.VisitMember(node);
        }
    }
}

用法是:

Expression<Func<ErrorModel, bool>> where = (c) => c.Created <= DateTime.UtcNow && c.ErrorCode == "code";
var replaced = where.ReplaceParameter<ErrorModel, Error>();

正如Evk所写,你需要一个ExpressionVisitor.

对于比Evk支持的表达式更复杂的表达式,比如多个参数,使用构造函数和成员初始化列表( new { Prop1 = true } ):

public class TypeReplacer : ExpressionVisitor
{
    public readonly Dictionary<Type, Type> Conversions = new Dictionary<Type, Type>();
    private readonly Dictionary<Expression, Expression> ParameterConversions = new Dictionary<Expression, Expression>();

    protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
    {
        Type to;

        if (Conversions.TryGetValue(node.Member.DeclaringType, out to))
        {
            var member = ConvertMember(node.Member, to);
            node = Expression.Bind(member, node.Expression);
        }

        return base.VisitMemberAssignment(node);
    }

    public override Expression Visit(Expression node)
    {
        if (node.NodeType == ExpressionType.Lambda)
        {
            var lambda = (LambdaExpression)node;

            var parameters = lambda.Parameters.ToArray();

            for (int i = 0; i < parameters.Length; i++)
            {
                ParameterExpression parameter = parameters[i];

                Type to;

                if (Conversions.TryGetValue(parameter.Type, out to))
                {
                    var oldParameter = parameter;
                    parameter = Expression.Parameter(to, parameter.Name);
                    ParameterConversions.Add(oldParameter, parameter);
                }
            }

            var body = base.Visit(lambda.Body);

            for (int i = 0; i < parameters.Length; i++)
            {
                var parameter = (ParameterExpression)base.Visit(parameters[i]);
                parameters[i] = parameter;
            }

            // Handling of the delegate type
            var arguments = node.Type.GetGenericArguments();

            {
                Type to;

                for (int i = 0; i < arguments.Length; i++)
                {
                    if (Conversions.TryGetValue(arguments[i], out to))
                    {
                        arguments[i] = to;
                    }
                }
            }

            var delegateType = node.Type.GetGenericTypeDefinition().MakeGenericType(arguments);

            node = Expression.Lambda(delegateType, body, parameters);
            return node;
        }

        return base.Visit(node);
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        Type to;

        if (Conversions.TryGetValue(node.Type, out to))
        {
            node = Expression.Constant(node.Value, to);
        }

        return base.VisitConstant(node);
    }

    protected override Expression VisitNew(NewExpression node)
    {
        Type to;

        if (Conversions.TryGetValue(node.Type, out to))
        {
            var constructor = node.Constructor;

            BindingFlags bf = (constructor.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
                BindingFlags.Instance;

            var parameters = constructor.GetParameters();
            var types = Array.ConvertAll(parameters, x => x.ParameterType);

            var constructor2 = to.GetConstructor(bf, null, types, null);

            if (node.Members != null)
            {
                // Shouldn't happen. node.Members != null with anonymous types
                IEnumerable<MemberInfo> members = node.Members.Select(x => ConvertMember(x, to));
                node = Expression.New(constructor2, node.Arguments, members);
            }
            else
            {
                node = Expression.New(constructor2, node.Arguments);
            }
        }

        return base.VisitNew(node);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        Type to = null;

        Expression expression = null;

        if (node.Expression != null)
        {
            if (ParameterConversions.TryGetValue(node.Expression, out expression))
            {
                to = expression.Type;
            }
        }

        if (to != null || (node.Expression == null && Conversions.TryGetValue(node.Member.DeclaringType, out to)))
        {
            MemberInfo member = ConvertMember(node.Member, to);

            node = Expression.MakeMemberAccess(expression, member);
        }

        return base.VisitMember(node);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        Expression to;

        if (ParameterConversions.TryGetValue(node, out to))
        {
            node = (ParameterExpression)to;
        }

        return base.VisitParameter(node);
    }

    // Conversion of method/property/field accessor (supported indexers)
    private static MemberInfo ConvertMember(MemberInfo member, Type to)
    {
        switch (member.MemberType)
        {
            case MemberTypes.Field:
                {
                    var field = (FieldInfo)member;

                    BindingFlags bf = (field.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
                        (field.IsStatic ? BindingFlags.Static : BindingFlags.Instance);

                    var field2 = to.GetField(member.Name, bf);

                    return field2;
                }

            case MemberTypes.Property:
                {
                    var prop = (PropertyInfo)member;

                    var method = prop.GetMethod ?? prop.SetMethod;

                    BindingFlags bf = (method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
                        (method.IsStatic ? BindingFlags.Static : BindingFlags.Instance);

                    var indexes = prop.GetIndexParameters();

                    var types = Array.ConvertAll(indexes, x => x.ParameterType);

                    var property2 = to.GetProperty(member.Name, bf, null, prop.PropertyType, types, null);

                    return property2;
                }

            case MemberTypes.Method:
                {
                    var method = (MethodInfo)member;

                    BindingFlags bf = (method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
                        (method.IsStatic ? BindingFlags.Static : BindingFlags.Instance);

                    var parameters = method.GetParameters();
                    var types = Array.ConvertAll(parameters, x => x.ParameterType);

                    var method2 = to.GetMethod(member.Name, bf, null, types, null);

                    return method2;
                }

            default:
                throw new NotSupportedException(member.MemberType.ToString());
        }
    }
}

你这样使用:

Expression<Func<Class1a, Class1b, bool>> exp1 = (x, y) => x.Prop1;

var visitor = new TypeReplacer();
visitor.Conversions.Add(typeof(Class1a), typeof(Class2a));
visitor.Conversions.Add(typeof(Class1b), typeof(Class2b));

var result = (Expression<Func<Class2a, Class2b, bool>>)visitor.Visit(exp1);

请注意,此代码显示在最复杂的情​​况下可以进行此转换的痛苦程度......此代码完整...此处未涉及许多角落案例! (例如,不支持: x.SomeMethod(new ClassOriginal()) -> x.SomeMethod(new ClassConverted()

增加了对支持:

x => x;
x => null;

(他们需要特殊处理)

我会将那些“相同属性”提取到接口,并让类实现它......

我前段时间遇到了同样的问题,我找到的解决方案是将主要实体转换为目标实体。 但它只是在属性具有相同名称时才起作用,否则您将需要额外的工作来映射过滤器上的属性。

所以我们去代码,试试吧:

  • 首先创建一个扩展类

     public static class ExtensionToExpression { public static Expression<Func<TTo, bool>> Converter<TFrom, TTo>(this Expression<Func<TFrom, bool>> expression, TTo type) where TTo : TFrom { // here we get the expression parameter the x from (x) => .... var parameterName = expression.Parameters.First().Name; // create the new parameter from the correct type ParameterExpression parameter = Expression.Parameter(typeof(TTo), parameterName); // asigne to allow the visit from or visitor Expression body = new ConvertVisitor(parameter).Visit(expression.Body); // recreate the expression return Expression.Lambda<Func<TTo, bool>>(body, parameter); } } 

现在创建de Visitor类

public class ConvertVisitor : ExpressionVisitor
{
    private ParameterExpression Parameter;

    public Visitante(ParameterExpression parameter)
    {
        Parameter = parameter;
    }

    protected override Expression VisitParameter(ParameterExpression item)
    {
        // we just check the parameter to return the new value for them
        if(!item.Name.Equals(Parameter.Name))
            return item;
        return Parameter;
    }
 }

并使用://添加使用以使用您的扩展方法

public void YouMethod(Expression<Func<TEntity, bool>> filter)
{
     var whereExpression = filter.Convert(default(TDbEntity));
     var result = yourContext.Where(whereExpression).ToArray();
     // do something
}

我希望它可以帮到你。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM