I use Automapper and a three-tier architecture in the application. I have a method that accepts Expression<Func<T, bool>> predicate as a parameter. To pass this parameter with the desired type Π’ to another level, I do the remapping. I created the ExpressionExtension class , which is used to return the transformed Expression :

 /// <summary> /// A class which contains extension methods for <see cref="Expression"/> and <see cref="Expression{TDelegate}"/> instances. /// </summary> public static class ExpressionExtensions { /// <summary> /// Remaps all property access from type <typeparamref name="TSource"/> to <typeparamref name="TDestination"/> in <paramref name="expression"/>. /// </summary> /// <typeparam name="TSource">The type of the source element.</typeparam> /// <typeparam name="TDestination">The type of the destination element.</typeparam> /// <typeparam name="TResult">The type of the result from the lambda expression.</typeparam> /// <param name="expression">The <see cref="Expression{TDelegate}"/> to remap the property access in.</param> /// <returns>An <see cref="Expression{TDelegate}"/> equivalent to <paramref name="expression"/>, but applying to elements of type <typeparamref name="TDestination"/> instead of <typeparamref name="TSource"/>.</returns> public static Expression<Func<TDestination, TResult>> RemapForType<TSource, TDestination, TResult>( this Expression<Func<TSource, TResult>> expression) { Contract.Requires(expression != null); Contract.Ensures(Contract.Result<Expression<Func<TDestination, TResult>>>() != null); var newParameter = Expression.Parameter(typeof(TDestination)); Contract.Assume(newParameter != null); var visitor = new AutoMapVisitor<TSource, TDestination>(newParameter); var remappedBody = visitor.Visit(expression.Body); if (remappedBody == null) { throw new InvalidOperationException("Unable to remap expression"); } return Expression.Lambda<Func<TDestination, TResult>>(remappedBody, newParameter); } } 

And the code for AutoMapVisitor:

 /// <summary> /// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>. /// </summary> /// <typeparam name="TSource">The type of the source element.</typeparam> /// <typeparam name="TDestination">The type of the destination element.</typeparam> public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor { private readonly TypeMap _typeMap; private readonly List<TypeMap> _typeMaps = new List<TypeMap>(); private readonly Dictionary<Type, ParameterExpression> parameterMap = new Dictionary<Type, ParameterExpression>(); private Dictionary<MemberInfo, Expression> memberMap = new Dictionary<MemberInfo, Expression>(); /// <summary> /// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class. /// </summary> /// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param> public AutoMapVisitor(ParameterExpression newParameter) { _typeMap = Mapper.FindTypeMapFor<TSource, TDestination>(); Contract.Assume(_typeMap != null); _typeMaps.Add(_typeMap); _typeMaps.AddRange(FillNestedTypeMaps(_typeMap)); parameterMap.Add(newParameter.Type, newParameter); // main parameter which we don't need to recreate foreach (TypeMap map in _typeMaps) { if (parameterMap.ContainsKey(map.DestinationType)) continue; parameterMap.Add(map.DestinationType, Expression.Parameter(map.DestinationType, map.DestinationType.Name.ToLower())); } } /// <summary> /// Π½Π°Ρ…ΠΎΠ΄ΠΈΠΌ всС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ ΠΌΠ°ΠΏΠΏΠΈΠ½Π³ΠΈ Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ Π²Π½ΠΈΠ· ΠΏΠΎ ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ свойств класса источника /// </summary> /// <param name="tMap">ΠžΠ±ΡŠΠ΅ΠΊΡ‚ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ, содСрТащий ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ Ρ‚ΠΈΠΏΠ΅ источника ΠΈ Ρ‚ΠΈΠΏΠ΅ назначСния</param> private IEnumerable<TypeMap> FillNestedTypeMaps(TypeMap tMap) { List<TypeMap> result = new List<TypeMap>(); // 1 where: ΠΌΠ°ΠΏΠΏΠΈΠ½Π³ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΌΠ΅ΠΆΠ΄Ρƒ классами // 2 where: ΠΌΠ°ΠΏΠΏΠΈΠ½Π³ Π½Π΅ Ρ€Π°Π²Π΅Π½ входящСму ΠΈ ReverseMap Π·Π½Π°Ρ‡Π΅Π½ΠΈΡŽ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π½Π° Π²Ρ…ΠΎΠ΄Π΅: A -> B, Ρ‚ΠΎΠ³Π΄Π° ΠΎΡ‚ΠΏΠ°Π΄ΡƒΡ‚ значСния A -> B ΠΈ B -> A ΠΈΠ· ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ // 3 where: Π±Π΅Ρ€Π΅ΠΌ Ρ‚Π΅ свойства, Ρ‚ΠΈΠΏ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… совпадаСт с входящим Ρ‚ΠΈΠΏΠΎΠΌ источника ΠΈΠ»ΠΈ, Ссли это ΠΎΠ±ΠΎΠ±Ρ‰Π΅Π½Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ, с Π΅Π³ΠΎ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ ΠΈ Ρ‚ΠΎΠΆΠ΅ самоС для Ρ‚ΠΈΠΏΠ° назначСния IEnumerable<PropertyMap> pMaps = tMap.GetPropertyMaps(); var list = Mapper.GetAllTypeMaps() .Where( map => map.SourceType.IsClass && map.DestinationType.IsClass) .Where(map => !(map.Equals(tMap) || (map.SourceType == tMap.DestinationType && map.DestinationType == tMap.SourceType))) .Where( map => pMaps .Any( pi => { var pis = pi.SourceMember as PropertyInfo; if (pis == null) return false; bool forSource = pis.PropertyType == map.SourceType || (pis.PropertyType.IsGenericType && pis.PropertyType.GetGenericArguments()[0] == map.SourceType); bool forDestination = pi.DestinationPropertyType == map.DestinationType || (pi.DestinationPropertyType.IsGenericType && pi.DestinationPropertyType.GetGenericArguments()[0] == map.DestinationType); return forSource && forDestination; })) .ToList(); if (list.Count > 0) { result.AddRange(list); foreach (TypeMap typeMap in list) { result.AddRange(FillNestedTypeMaps(typeMap)); } } return result; } private Type Map(Type type) { var tMap = _typeMaps.FirstOrDefault(map => map.SourceType == type); Contract.Assume(tMap != null); return tMap.DestinationType; } private ParameterExpression Map(ParameterExpression parameter) { var mappedType = Map(parameter.Type); ParameterExpression mappedParameter; if (!parameterMap.TryGetValue(mappedType, out mappedParameter)) parameterMap.Add(mappedType, mappedParameter = Expression.Parameter(mappedType, parameter.Name)); return mappedParameter; } private Expression Map(MemberInfo mi, Expression exp) { Expression val; if (!memberMap.TryGetValue(mi, out val)) { foreach (PropertyMap propertyMap in _typeMaps.Select(map => map.GetPropertyMaps().SingleOrDefault(m => m.SourceMember == mi)) .Where(propertyMap => propertyMap != null)) { memberMap.Add(mi, val = Expression.PropertyOrField(exp, propertyMap.DestinationProperty.MemberInfo.Name)); break; } } return val; } /// <summary> /// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>. /// </summary> /// <returns> /// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression. /// </returns> /// <param name="node">The expression to visit.</param> protected override Expression VisitMember(MemberExpression node) { var expression = Visit(node.Expression); if (expression == node.Expression) return node; return Map(node.Member, expression); } protected override Expression VisitLambda<T>(Expression<T> node) { return Expression.Lambda(Visit(node.Body), node.Parameters.Select(Map)); } protected override Expression VisitParameter(ParameterExpression node) { return Map(node); } protected override Expression VisitMethodCall(MethodCallExpression node) { // if static object and generic method if (node.Object == null && node.Method.IsGenericMethod) { // Static generic method var args = Visit(node.Arguments); var genericArgs = node.Method.GetGenericArguments().Select(Map).ToArray(); var method = node.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArgs); return Expression.Call(method, args); } return base.VisitMethodCall(node); } } 

Now after remapping throws Exception :

variable 'person' of type 'Reestr.DAL.Entities.Person' referenced from scope '', but it is not defined

It is curious that this exception is thrown only if we have an embedded lambda available, for example:

 Expression<Func<TSource,TResult>> a = val => val.FullName == "ABC" && val.SomeEnumerableProperty.Any(inner => inner.City == "Moscow"); 

If we just filled the LINQ search query with normal properties:

 Expression<Func<TSource,TResult>> a = val => val.FullName == "ABC"; 

then everything works well, the exception is not thrown. It seems that there is no link to the object or another parameter has somehow been created somewhere, but I cannot understand where (probably when remapping the internal lambda). What can be suggestions for a solution. Thank you

  • Copy the text of the question entirely, do not force people to follow the link - PashaPash ♦
  • @PashaPash, understood, and need to translate? - Dmitry
  • yes, otherwise they will close closing - PashaPash ♦

0