如何更正表达式的替换类型?

时间:2015-05-29 19:45:06

标签: c# linq-expressions

我有两个班级:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // configure the precision to match what is specified in the database
    modelBuilder.Entity<MyEntity>().Property(p => p.MyDecimalValue).HasPrecision(19, 5);
}

我有功能:

public class DalMembershipUser
{
        public string UserName { get; set; }
        //other members
}

public class MembershipUser
{
        public string UserName { get; set; }
        //other members
}

目前的实施:

    public IEnumerable<DalMembershipUser> GetMany(Expression<Func<DalMembershipUser, bool>> predicate)
    {
      //but here i can use only Func<MembershipUser, bool>
      //so i made transformation
        query = query.Where(ExpressionTransformer<DalMembershipUser,MembershipUser>.Tranform(predicate));
}

//某个地方:public static class ExpressionTransformer<TFrom, TTo> { public class Visitor : ExpressionVisitor { private ParameterExpression _targetParameterExpression; public Visitor(ParameterExpression parameter) { _targetParameterExpression = parameter; } protected override Expression VisitParameter(ParameterExpression node) { return _targetParameterExpression; } } public static Expression<Func<TTo, bool>> Tranform(Expression<Func<TFrom, bool>> expression) { ParameterExpression parameter = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name); Expression body = expression.Body; new Visitor(parameter).Visit(expression.Body); return Expression.Lambda<Func<TTo, bool>>(body, parameter); } }

例外:属性&#39; System.String用户名&#39;未定义类型&#39; MembershipUser&#39;
at line:new Visitor(参数).Visit(expression.Body);

2 个答案:

答案 0 :(得分:0)

您需要使用Visit方法返回的结果表达式。

只需改变:

Expression body = expression.Body;
new Visitor(parameter).Visit(expression.Body);

通过

Expression body = new Visitor(parameter).Visit(expression.Body);

答案 1 :(得分:0)

最后它起作用了。但仍然不明白为什么明确的参数创建: Expression.Parameter(typeof(TTo), from.Parameters[i].Name);不起作用,需要提取。

public static class ExpressionHelper
    {

        public static Expression<Func<TTo, bool>> TypeConvert<TFrom, TTo>(
            this Expression<Func<TFrom, bool>> from)
        {
            if (from == null) return null;

            return ConvertImpl<Func<TFrom, bool>, Func<TTo, bool>>(from);
        }

        private static Expression<TTo> ConvertImpl<TFrom, TTo>(Expression<TFrom> from)
            where TFrom : class
            where TTo : class
        {
            // figure out which types are different in the function-signature

            var fromTypes = from.Type.GetGenericArguments();
            var toTypes = typeof(TTo).GetGenericArguments();

            if (fromTypes.Length != toTypes.Length)
                throw new NotSupportedException("Incompatible lambda function-type signatures");

            Dictionary<Type, Type> typeMap = new Dictionary<Type, Type>();
            for (int i = 0; i < fromTypes.Length; i++)
            {
                if (fromTypes[i] != toTypes[i])
                    typeMap[fromTypes[i]] = toTypes[i];
            }

            // re-map all parameters that involve different types
            Dictionary<Expression, Expression> parameterMap = new Dictionary<Expression, Expression>();
            ParameterExpression[] newParams = GenerateParameterMap<TFrom>(from, typeMap, parameterMap);

            // rebuild the lambda
            var body = new TypeConversionVisitor<TTo>(parameterMap).Visit(from.Body);
            return Expression.Lambda<TTo>(body, newParams);
        }

        private static ParameterExpression[] GenerateParameterMap<TFrom>(
            Expression<TFrom> from,
            Dictionary<Type, Type> typeMap,
            Dictionary<Expression, Expression> parameterMap
        ) 
            where TFrom : class
        {
            var newParams = new ParameterExpression[from.Parameters.Count];

            for (int i = 0; i < newParams.Length; i++)
            {
                Type newType;
                if (typeMap.TryGetValue(from.Parameters[i].Type, out newType))
                {
                    parameterMap[from.Parameters[i]] = newParams[i] = Expression.Parameter(newType, from.Parameters[i].Name);
                }
            }
            return newParams;
        }


        class TypeConversionVisitor<T> : ExpressionVisitor
        {
            private readonly Dictionary<Expression, Expression> parameterMap;

            public TypeConversionVisitor(Dictionary<Expression, Expression> parameterMap)
            {
                this.parameterMap = parameterMap;
            }

            protected override Expression VisitParameter(ParameterExpression node)
            {
                // re-map the parameter
                Expression found;
                if (!parameterMap.TryGetValue(node, out found))
                    found = base.VisitParameter(node);
                return found;
            }
            public override Expression Visit(Expression node)
            {
                LambdaExpression lambda = node as LambdaExpression;
                if (lambda != null && !parameterMap.ContainsKey(lambda.Parameters.First()))
                {
                    return new TypeConversionVisitor<T>(parameterMap).Visit(lambda.Body);
                }
                return base.Visit(node);
            }

            protected override Expression VisitMember(MemberExpression node)
            {
                // re-perform any member-binding
                var expr = Visit(node.Expression);
                if (expr.Type != node.Type)
                {
                    if (expr.Type.GetMember(node.Member.Name).Any())
                    {
                        MemberInfo newMember = expr.Type.GetMember(node.Member.Name).Single();
                        return Expression.MakeMemberAccess(expr, newMember);
                    }
                }
                return base.VisitMember(node);
            }
        }
    }