Skip to content

Commit 698f686

Browse files
committed
Refactor expression extensions; fix typos and errors
- Consolidated all expression extension methods into ExpressionExtensions.cs; deleted VisitorExtensions.cs. - Added ExpressionHelpers.cs for member access and parameter replacement helpers. - Fixed resource typos ("deledate" → "delegate", "rempa" → "map"). - Added new error message for invalid type mappings. - Updated TypeMappingsManager to throw on mismatched Expression<> types and always add type mappings for argument pairs. - Cleaned up XpressionMapperVisitor.cs, fixed VisitParameter bug, and removed unused ConvertTypesIfNecessary. - Updated unit tests for corrected error messages. - Improves code organization, error clarity, and maintainability.
1 parent 4896189 commit 698f686

File tree

9 files changed

+215
-222
lines changed

9 files changed

+215
-222
lines changed
Lines changed: 148 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,172 @@
1-
using System;
1+
using AutoMapper.Extensions.ExpressionMapping.Structures;
2+
using AutoMapper.Internal;
3+
using System;
24
using System.Collections.Generic;
35
using System.Linq;
46
using System.Linq.Expressions;
57
using System.Reflection;
6-
using AutoMapper.Internal;
8+
using System.Runtime.CompilerServices;
79

810
namespace AutoMapper.Extensions.ExpressionMapping
911
{
1012
using static Expression;
1113

1214
internal static class ExpressionExtensions
1315
{
14-
public static Expression MemberAccesses(this IEnumerable<MemberInfo> members, Expression obj) =>
15-
members.Aggregate(obj, (expression, member) => MakeMemberAccess(expression, member));
16-
}
16+
public static Expression ConvertTypeIfNecessary(this Expression expression, Type memberType)
17+
{
18+
if (memberType == expression.Type)
19+
return expression;
1720

18-
internal static class ExpressionHelpers
19-
{
20-
public static MemberExpression MemberAccesses(string members, Expression obj) =>
21-
(MemberExpression)GetMemberPath(obj.Type, members).MemberAccesses(obj);
21+
expression = expression.GetUnconvertedExpression();
22+
if (memberType != expression.Type)
23+
return Expression.Convert(expression, memberType);
24+
25+
return expression;
26+
}
27+
28+
/// <summary>
29+
/// Returns the first ancestor node that is not a MemberExpression.
30+
/// </summary>
31+
/// <param name="expression"></param>
32+
/// <returns></returns>
33+
public static Expression GetBaseOfMemberExpression(this MemberExpression expression)
34+
{
35+
if (expression.Expression == null)
36+
return null;
37+
38+
return expression.Expression.NodeType == ExpressionType.MemberAccess
39+
? GetBaseOfMemberExpression((MemberExpression)expression.Expression)
40+
: expression.Expression;
41+
}
42+
43+
public static MemberExpression GetMemberExpression(this Expression expr)
44+
=> expr?.GetUnconvertedExpression() as MemberExpression;
45+
46+
public static MemberExpression GetMemberExpression(this LambdaExpression expr)
47+
=> expr.Body.GetUnconvertedExpression() as MemberExpression;
2248

23-
public static Expression ReplaceParameters(this LambdaExpression exp, params Expression[] replace)
49+
/// <summary>
50+
/// For the given a Lambda Expression, returns the fully qualified name of the member starting with the immediate child member of the parameter
51+
/// </summary>
52+
/// <param name="expr"></param>
53+
/// <returns></returns>
54+
public static string GetMemberFullName(this LambdaExpression expr)
2455
{
25-
var replaceExp = exp.Body;
26-
for (var i = 0; i < Math.Min(replace.Length, exp.Parameters.Count); i++)
27-
replaceExp = Replace(replaceExp, exp.Parameters[i], replace[i]);
28-
return replaceExp;
56+
if (expr.Body.NodeType == ExpressionType.Parameter)
57+
return string.Empty;
58+
MemberExpression me = expr.Body.NodeType switch
59+
{
60+
ExpressionType.Convert or ExpressionType.ConvertChecked or ExpressionType.TypeAs => expr.Body.GetUnconvertedExpression() as MemberExpression,
61+
_ => expr.Body as MemberExpression,
62+
};
63+
return me.GetPropertyFullName();
2964
}
3065

31-
public static Expression Replace(this Expression exp, Expression old, Expression replace) => new ReplaceExpressionVisitor(old, replace).Visit(exp);
66+
/// <summary>
67+
/// Returns the ParameterExpression for the LINQ parameter.
68+
/// </summary>
69+
/// <param name="expression"></param>
70+
/// <returns></returns>
71+
public static ParameterExpression GetParameterExpression(this Expression expression)
72+
{
73+
if (expression == null)
74+
return null;
75+
76+
//the node represents parameter of the expression
77+
switch (expression.NodeType)
78+
{
79+
case ExpressionType.Parameter:
80+
return (ParameterExpression)expression;
81+
case ExpressionType.Quote:
82+
return GetParameterExpression(((UnaryExpression)expression).Operand);
83+
case ExpressionType.Lambda:
84+
return GetParameterExpression(((LambdaExpression)expression).Body);
85+
case ExpressionType.ConvertChecked:
86+
case ExpressionType.Convert:
87+
var ue = expression as UnaryExpression;
88+
return GetParameterExpression(ue?.Operand);
89+
case ExpressionType.TypeAs:
90+
return ((UnaryExpression)expression).Operand.GetParameterExpression();
91+
case ExpressionType.TypeIs:
92+
return ((TypeBinaryExpression)expression).Expression.GetParameterExpression();
93+
case ExpressionType.MemberAccess:
94+
return GetParameterExpression(((MemberExpression)expression).Expression);
95+
case ExpressionType.Call:
96+
var methodExpression = expression as MethodCallExpression;
97+
var parentExpression = methodExpression?.Object;//Method is an instance method
98+
99+
var isExtension = methodExpression != null && methodExpression.Method.IsDefined(typeof(ExtensionAttribute), true);
100+
if (isExtension && parentExpression == null && methodExpression.Arguments.Count > 0)
101+
parentExpression = methodExpression.Arguments[0];//Method is an extension method based on the type of methodExpression.Arguments[0].
102+
103+
if (parentExpression == null)
104+
return null;
32105

33-
private static IEnumerable<MemberInfo> GetMemberPath(Type type, string fullMemberName)
106+
return GetParameterExpression(parentExpression);
107+
}
108+
109+
return null;
110+
}
111+
112+
/// <summary>
113+
/// Returns the fully qualified name of the member starting with the immediate child member of the parameter
114+
/// </summary>
115+
/// <param name="expression"></param>
116+
/// <returns></returns>
117+
public static string GetPropertyFullName(this Expression expression)
34118
{
35-
MemberInfo property = null;
36-
foreach (var memberName in fullMemberName.Split('.'))
119+
if (expression == null)
120+
return string.Empty;
121+
122+
const string period = ".";
123+
124+
//the node represents parameter of the expression
125+
switch (expression.NodeType)
37126
{
38-
var currentType = GetCurrentType(property, type);
39-
yield return property = currentType.GetFieldOrProperty(memberName);
127+
case ExpressionType.MemberAccess:
128+
var memberExpression = (MemberExpression)expression;
129+
var parentFullName = memberExpression.Expression.GetPropertyFullName();
130+
return string.IsNullOrEmpty(parentFullName)
131+
? memberExpression.Member.Name
132+
: string.Concat(memberExpression.Expression.GetPropertyFullName(), period, memberExpression.Member.Name);
133+
default:
134+
return string.Empty;
40135
}
41136
}
42137

43-
private static Type GetCurrentType(MemberInfo member, Type type)
44-
=> member?.GetMemberType() ?? type;
138+
public static Expression GetUnconvertedExpression(this Expression expression)
139+
{
140+
return expression.NodeType switch
141+
{
142+
ExpressionType.Convert or ExpressionType.ConvertChecked or ExpressionType.TypeAs => ((UnaryExpression)expression).Operand.GetUnconvertedExpression(),
143+
_ => expression,
144+
};
145+
}
146+
147+
/// <summary>
148+
/// Determines whether the specified type is an enumeration type.
149+
/// </summary>
150+
/// <param name="type">The type to evaluate. This can be a nullable type, in which case the underlying type is checked.</param>
151+
/// <returns>true if the specified type is an enumeration; otherwise, false.</returns>
152+
public static bool IsEnumType(this Type type)
153+
{
154+
if (type.IsNullableType())
155+
type = Nullable.GetUnderlyingType(type);
156+
157+
return type.IsEnum();
158+
}
159+
160+
/// <summary>
161+
/// Adds member expressions to an existing expression.
162+
/// </summary>
163+
/// <param name="exp"></param>
164+
/// <param name="list"></param>
165+
/// <returns></returns>
166+
public static MemberExpression MemberAccesses(this Expression exp, List<PropertyMapInfo> list) =>
167+
(MemberExpression)list.SelectMany(propertyMapInfo => propertyMapInfo.DestinationPropertyInfos).MemberAccesses(exp);
168+
169+
public static Expression MemberAccesses(this IEnumerable<MemberInfo> members, Expression obj) =>
170+
members.Aggregate(obj, MakeMemberAccess);
45171
}
46172
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using AutoMapper.Internal;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq.Expressions;
5+
using System.Reflection;
6+
7+
namespace AutoMapper.Extensions.ExpressionMapping
8+
{
9+
internal static class ExpressionHelpers
10+
{
11+
public static MemberExpression MemberAccesses(string members, Expression obj) =>
12+
(MemberExpression)GetMemberPath(obj.Type, members).MemberAccesses(obj);
13+
14+
public static Expression ReplaceParameters(this LambdaExpression exp, params Expression[] replace)
15+
{
16+
var replaceExp = exp.Body;
17+
for (var i = 0; i < Math.Min(replace.Length, exp.Parameters.Count); i++)
18+
replaceExp = Replace(replaceExp, exp.Parameters[i], replace[i]);
19+
return replaceExp;
20+
}
21+
22+
public static Expression Replace(this Expression exp, Expression old, Expression replace) => new ReplaceExpressionVisitor(old, replace).Visit(exp);
23+
24+
private static IEnumerable<MemberInfo> GetMemberPath(Type type, string fullMemberName)
25+
{
26+
MemberInfo property = null;
27+
foreach (var memberName in fullMemberName.Split('.'))
28+
{
29+
var currentType = GetCurrentType(property, type);
30+
yield return property = currentType.GetFieldOrProperty(memberName);
31+
}
32+
}
33+
34+
private static Type GetCurrentType(MemberInfo member, Type type)
35+
=> member?.GetMemberType() ?? type;
36+
}
37+
}

0 commit comments

Comments
 (0)