Перевод дерева выражений из типа в другой тип со сложными отображениями
вдохновленныйэтот ответ Я пытаюсь сопоставить свойство класса модели с выражением, основанным на фактической сущности. Это два участвующих класса:
<code>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; } } </code>
Пример возможного выражения, которое я хотел бы преобразовать:
<code>Expression<Func<CustomerModel, bool>> from = model => model.HasEvenId; Expression<Func<Customer, bool>> to = entity => ((entity.Id % 2) == 0); </code>
Проблема заключается в том, что мне нужно предоставить конечную точку OData через ASP.NET WebAPI, но мне нужно выполнить некоторые операции над объектами, прежде чем я смогу их получить, поэтому мне нужен класс модели и необходимость переводить выражение на основе модели, которая Я мог получить запрос OData в выражении, основанном на сущности, который я использовал бы для запроса EF4.
Вот где я дошел до сих пор:
<code>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(); } </code>
где:
ODataFilterExtractor is a class that extract the $filter expression from the RequestMessage we receive; ParameterChangeVisitor just changes all the ParameterExpression to a new one having the provided string as parameter name;Кроме того, я изменил метод VisitMember ответа, связанного выше, таким образом:
<code>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); } </code>
Спасибо за помощь.