加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

linq – 将表达式树从一种类型转换为另一种具有复杂映射的类型

发布时间:2020-12-16 07:10:29 所属栏目:asp.Net 来源:网络整理
导读:灵感来自 this answer我试图将模型类上的属性映射到基于实际实体的表达式. 这是涉及的两个类: public class Customer{ public string FirstName { get; set; } public string LastName { get; set; } public int Id { get; set; } public DateTime? BirthDa
灵感来自 this answer我试图将模型类上的属性映射到基于实际实体的表达式.
这是涉及的两个类:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Id { get; set; }
    public DateTime? BirthDate { get; set; }
    public int CustomerTypeId { get; set; }
}

public class CustomerModel
{
    ...
    public bool HasEvenId { get; set; }
}

我想要转换的可能表达式的示例是:

Expression<Func<CustomerModel,bool>> from = model => model.HasEvenId;
Expression<Func<Customer,bool>> to = entity => ((entity.Id % 2) == 0);

问题是我必须通过ASP.NET WebAPI公开OData端点,但是我需要先对实体进行一些操作,因此需要一个模型类,需要根据模型转换表达式我可以在基于实体的表达式中接收OData查询,我将用它来查询EF4.

这是我到目前为止的地方:

private static readonly Dictionary<Expression,Expression> Mappings = GetMappings();

private static Dictionary<Expression,Expression> GetMappings()
{
    var mappings = new Dictionary<Expression,Expression>();

    var mapping = GetMappingFor((CustomerModel model) => model.HasEvenId,(Customer customer) => (customer.Id%2) == 0);
    mappings.Add(mapping.Item1,mapping.Item2);

    return mappings;
}

private static Tuple<Expression,Expression> GetMappingFor<TFrom,TTo,TValue>(Expression<Func<TFrom,TValue>> fromExpression,Expression<Func<TTo,TValue>> toExpression)
{
    MemberExpression fromMemberExpression = (MemberExpression) fromExpression.Body;
    return Tuple.Create<Expression,Expression>(fromMemberExpression,toExpression);
}

public static Expression<Func<TTo,bool>> Translate<TFrom,TTo>(Expression<Func<TFrom,bool>> expression,Dictionary<Expression,Expression> mappings = null)
{
    if (expression == null)
        return null;

    string parameterName = expression.Parameters[0].Name;

    parameterName = string.IsNullOrWhiteSpace(parameterName) ? "p" : parameterName;

    var param = Expression.Parameter(typeof(TTo),parameterName);
    var subst = new Dictionary<Expression,Expression> { { expression.Parameters[0],param } };
    ParameterChangeVisitor parameterChange = new ParameterChangeVisitor(parameterName);
    if (mappings != null)
        foreach (var mapp in mappings)
            subst.Add(mapp.Key,parameterChange.Visit(mapp.Value));

    var visitor = new TypeChangeVisitor(typeof(TFrom),typeof(TTo),subst);
    return Expression.Lambda<Func<TTo,bool>>(visitor.Visit(expression.Body),param);
}

public IQueryable<CustomerModel> Get()
{
    var filterExtractor = new ODataFilterExtractor<CustomerModel>();
    Expression<Func<CustomerModel,bool>> expression = filterExtractor.Extract(Request);
    Expression<Func<Customer,bool>> translatedExpression = Translate<CustomerModel,Customer>(expression,Mappings);

    IQueryable<Customer> query = _context.Customers;

    if (translatedExpression != null)
        query = query.Where(translatedExpression);

    var finalQuery = from item in query.AsEnumerable() 
                     select new CustomerModel()
                        {
                            FirstName = item.FirstName,LastName = item.LastName,Id = item.Id,BirthDate = item.BirthDate,CustomerTypeId = item.CustomerTypeId,HasEvenId = (item.Id % 2 ) == 0
                        };

    return finalQuery.AsQueryable();
}

哪里:

> ODataFilterExtractor是一个从我们收到的RequestMessage中提取$filter表达式的类;
> ParameterChangeVisitor只是将所有的ParameterExpression更改为一个新的,其中提供的字符串作为参数名称;

另外,我以这种方式更改了上面链接的答案的VisitMember方法:

protected override Expression VisitMember(MemberExpression node)
{
    // if we see x.Name on the old type,substitute for new type
    if (node.Member.DeclaringType == _from)
    {
        MemberInfo toMember = _to.GetMember(node.Member.Name,node.Member.MemberType,BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SingleOrDefault();
        if (toMember != null)
        {
            return Expression.MakeMemberAccess(Visit(node.Expression),toMember);
        }
        else
        {
            if (_substitutions.Select(kvp => kvp.Key).OfType<MemberExpression>().Any(me => me.Member.Equals(node.Member)))
            {
                MemberExpression key = _substitutions.Select(kvp => kvp.Key).OfType<MemberExpression>().Single(me => me.Member.Equals(node.Member));
                Expression value = _substitutions[key];

                // What to return here?
                return Expression.Invoke(value);
            }
        }
    }
    return base.VisitMember(node);
}

谢谢你的帮助.

解决方法

我冒昧地修改你的代码只是一个头发,但这样做的伎俩,

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Id { get; set; }
    public DateTime? BirthDate { get; set; }
    public int CustomerTypeId { get; set; }
}

public class CustomerModel
{
    public string FullName { get; set; }
    public bool HasEvenId { get; set; }
}

sealed class AToBConverter<TA,TB> : ExpressionVisitor
{
    private readonly Dictionary<ParameterExpression,ParameterExpression> _parameters = new Dictionary<ParameterExpression,ParameterExpression>();
    private readonly Dictionary<MemberInfo,LambdaExpression> _mappings;

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node.Type == typeof(TA))
        {
            ParameterExpression parameter;
            if (!this._parameters.TryGetValue(node,out parameter))
            {
                this._parameters.Add(node,parameter = Expression.Parameter(typeof(TB),node.Name));
            }
            return parameter;
        }
        return node;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression == null || node.Expression.Type != typeof(TA))
        {
            return base.VisitMember(node);
        }
        Expression expression = this.Visit(node.Expression);
        if (expression.Type != typeof(TB))
        {
            throw new Exception("Whoops");
        }
        LambdaExpression lambdaExpression;
        if (this._mappings.TryGetValue(node.Member,out lambdaExpression))
        {
            return new SimpleExpressionReplacer(lambdaExpression.Parameters.Single(),expression).Visit(lambdaExpression.Body);
        }
        return Expression.Property(
            expression,node.Member.Name
        );
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        return Expression.Lambda(
            this.Visit(node.Body),node.Parameters.Select(this.Visit).Cast<ParameterExpression>()
        );
    }

    public AToBConverter(Dictionary<MemberInfo,LambdaExpression> mappings)
    {
        this._mappings = mappings;
    }
}

sealed class SimpleExpressionReplacer : ExpressionVisitor
{
    private readonly Expression _replacement;
    private readonly Expression _toFind;

    public override Expression Visit(Expression node)
    {
        return node == this._toFind ? this._replacement : base.Visit(node);
    }

    public SimpleExpressionReplacer(Expression toFind,Expression replacement)
    {
        this._toFind = toFind;
        this._replacement = replacement;
    }
}

class Program 
{
    private static Dictionary<MemberInfo,LambdaExpression> GetMappings()
    {
        var mappings = new Dictionary<MemberInfo,LambdaExpression>();
        var mapping = GetMappingFor(model => model.HasEvenId,customer => (customer.Id % 2) == 0);
        mappings.Add(mapping.Item1,mapping.Item2);
        mapping = GetMappingFor(model => model.FullName,customer => customer.FirstName + " " + customer.LastName);
        mappings.Add(mapping.Item1,mapping.Item2);
        return mappings;
    }

    private static Tuple<MemberInfo,LambdaExpression> GetMappingFor<TValue>(Expression<Func<CustomerModel,Expression<Func<Customer,TValue>> toExpression)
    {
        return Tuple.Create(((MemberExpression)fromExpression.Body).Member,(LambdaExpression)toExpression);
    }

    static void Main()
    {
        Expression<Func<CustomerModel,bool>> source = model => model.HasEvenId && model.FullName == "John Smith";
        Expression<Func<Customer,bool>> desiredResult = model => (model.Id % 2) == 0 && (model.FirstName + " " + model.LastName) == "John Smith";
        Expression output = new AToBConverter<CustomerModel,Customer>(GetMappings()).Visit(source);
        Console.WriteLine("The two expressions do {0}match.",desiredResult.ToString() == output.ToString() ? null : "not ");
        Console.ReadLine();
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读