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表达式的类; 另外,我以这种方式更改了上面链接的答案的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(); } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
相关内容
- asp.net-mvc-3 – 具有角色的AuthorizeAttribute但不对角色
- [ASP.NET Core 2.0 前方速报].NET Core 2.0.3&
- asp.net-mvc – 保持viewdata在RedirectToAction
- asp.net-mvc – .NET MVC / Entity Framework应用程序中的内
- 如何在使用ASP.NET友好URL时忽略某些路由?
- asp.net – 我想列出我的下拉列表中的所有国家/地区,我在哪
- .net – 编辑器模板不适用于DisplayFormat
- asp.net-mvc – SignalR 2不生成/ signalr / hubs
- asp.net – 我如何做一个已经内置到dll中的程序集,特别是fl
- asp.net-core – 实体框架核心服务默认生命周期