Skip to content

Commit 9498221

Browse files
authored
Merge pull request #3920 from AutoMapper/ProjectTo_NRE
2 parents aec0698 + 29fdb6e commit 9498221

File tree

74 files changed

+2497
-2854
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+2497
-2854
lines changed

Setup.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Import-Module BitsTransfer
2727
Start-BitsTransfer -Source https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi -Destination SqlLocalDB.msi
2828
Write-Host "Installing"
2929
Start-Process -FilePath "SqlLocalDB.msi" -Wait -ArgumentList "/qn", "/norestart", "/l*v SqlLocalDBInstall.log", "IACCEPTSQLLOCALDBLICENSETERMS=YES";
30+
<#
3031
Write-Host "Checking"
3132
sqlcmd -l 60 -S "(localdb)\MSSQLLocalDB" -Q "SELECT @@VERSION;"
32-
33+
#>

src/AutoMapper/Configuration/ConfigurationValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void AssertConfigurationExpressionIsValid(IEnumerable<TypeMap> typeMaps)
3333
{
3434
if (!_expression.AllowAdditiveTypeMapCreation)
3535
{
36-
var duplicateTypeMapConfigs = _expression.Profiles.Concat(new[] { (Profile)_expression })
36+
var duplicateTypeMapConfigs = _expression.Profiles.Append((Profile)_expression)
3737
.SelectMany(p => p.TypeMapConfigs, (profile, typeMap) => (profile, typeMap))
3838
.GroupBy(x => x.typeMap.Types)
3939
.Where(g => g.Count() > 1)

src/AutoMapper/Configuration/MapperConfiguration.cs

Lines changed: 167 additions & 168 deletions
Large diffs are not rendered by default.

src/AutoMapper/Configuration/MappingExpressionBase.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public interface ITypeMapConfiguration
2020
TypePair Types { get; }
2121
ITypeMapConfiguration ReverseTypeMap { get; }
2222
TypeMap TypeMap { get; }
23+
bool HasTypeConverter { get; }
2324
}
2425
public abstract class MappingExpressionBase : ITypeMapConfiguration
2526
{
@@ -42,6 +43,7 @@ protected MappingExpressionBase(MemberList memberList, TypePair types)
4243
protected bool Projection { get; set; }
4344
public TypePair Types => _types;
4445
public bool IsReverseMap { get; set; }
46+
public bool HasTypeConverter { get; protected set; }
4547
public TypeMap TypeMap { get; private set; }
4648
public Type SourceType => _types.SourceType;
4749
public Type DestinationType => _types.DestinationType;
@@ -451,11 +453,15 @@ public TMappingExpression ConstructUsing(Func<TSource, ResolutionContext, TDesti
451453
return this as TMappingExpression;
452454
}
453455

454-
public void ConvertUsing(Type typeConverterType)
455-
=> TypeMapActions.Add(tm => tm.TypeConverterType = typeConverterType);
456+
public void ConvertUsing(Type typeConverterType)
457+
{
458+
HasTypeConverter = true;
459+
TypeMapActions.Add(tm => tm.TypeConverterType = typeConverterType);
460+
}
456461

457462
public void ConvertUsing(Func<TSource, TDestination, TDestination> mappingFunction)
458463
{
464+
HasTypeConverter = true;
459465
TypeMapActions.Add(tm =>
460466
{
461467
Expression<Func<TSource, TDestination, ResolutionContext, TDestination>> expr =
@@ -467,6 +473,7 @@ public void ConvertUsing(Func<TSource, TDestination, TDestination> mappingFuncti
467473

468474
public void ConvertUsing(Func<TSource, TDestination, ResolutionContext, TDestination> mappingFunction)
469475
{
476+
HasTypeConverter = true;
470477
TypeMapActions.Add(tm =>
471478
{
472479
Expression<Func<TSource, TDestination, ResolutionContext, TDestination>> expr =
@@ -476,13 +483,11 @@ public void ConvertUsing(Func<TSource, TDestination, ResolutionContext, TDestina
476483
});
477484
}
478485

479-
public void ConvertUsing(ITypeConverter<TSource, TDestination> converter)
480-
{
481-
ConvertUsing(converter.Convert);
482-
}
486+
public void ConvertUsing(ITypeConverter<TSource, TDestination> converter) => ConvertUsing(converter.Convert);
483487

484488
public void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>
485489
{
490+
HasTypeConverter = true;
486491
TypeMapActions.Add(tm => tm.TypeConverterType = typeof(TTypeConverter));
487492
}
488493

@@ -517,8 +522,11 @@ public TMappingExpression IgnoreAllSourcePropertiesWithAnInaccessibleSetter()
517522

518523
private static IEnumerable<PropertyInfo> PropertiesWithAnInaccessibleSetter(Type type) => type.GetRuntimeProperties().Where(p => p.GetSetMethod() == null);
519524

520-
public void ConvertUsing(Expression<Func<TSource, TDestination>> mappingFunction) =>
525+
public void ConvertUsing(Expression<Func<TSource, TDestination>> mappingFunction)
526+
{
527+
HasTypeConverter = true;
521528
TypeMapActions.Add(tm => tm.CustomMapExpression = mappingFunction);
529+
}
522530

523531
public TMappingExpression AsProxy()
524532
{

src/AutoMapper/Execution/ExpressionBuilder.cs

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ public static class ExpressionBuilder
1616
{
1717
public static readonly MethodInfo ObjectToString = typeof(object).GetMethod(nameof(object.ToString));
1818
private static readonly MethodInfo DisposeMethod = typeof(IDisposable).GetMethod(nameof(IDisposable.Dispose));
19-
public static readonly Expression False = Constant(false, typeof(bool));
2019
public static readonly Expression True = Constant(true, typeof(bool));
2120
public static readonly Expression Null = Constant(null, typeof(object));
2221
public static readonly Expression Empty = Empty();
@@ -34,6 +33,12 @@ public static class ExpressionBuilder
3433
private static readonly MethodInfo CheckContextMethod = typeof(ResolutionContext).GetStaticMethod(nameof(ResolutionContext.CheckContext));
3534
private static readonly MethodInfo ContextMapMethod = typeof(ResolutionContext).GetInstanceMethod(nameof(ResolutionContext.MapInternal));
3635
private static readonly MethodInfo ArrayEmptyMethod = typeof(Array).GetStaticMethod(nameof(Array.Empty));
36+
private static readonly ParameterExpression Disposable = Variable(typeof(IDisposable), "disposableEnumerator");
37+
private static readonly ParameterExpression[] DisposableArray = new[] { Disposable };
38+
private static readonly Expression DisposeCall = IfNullElse(Disposable, Empty, Expression.Call(Disposable, DisposeMethod));
39+
private static readonly ParameterExpression Index = Variable(typeof(int), "sourceArrayIndex");
40+
private static readonly BinaryExpression ResetIndex = Assign(Index, Zero);
41+
private static readonly UnaryExpression IncrementIndex = PostIncrementAssign(Index);
3742

3843
public static Expression MapExpression(this IGlobalConfiguration configurationProvider, ProfileMap profileMap, TypePair typePair, Expression sourceParameter,
3944
MemberMap propertyMap = null, Expression destinationParameter = null)
@@ -47,7 +52,7 @@ public static Expression MapExpression(this IGlobalConfiguration configurationPr
4752
hasTypeConverter = typeMap.HasTypeConverter;
4853
if (!typeMap.HasDerivedTypesToInclude)
4954
{
50-
typeMap.Seal(configurationProvider);
55+
configurationProvider.Seal(typeMap);
5156
mapExpression = typeMap.MapExpression?.ConvertReplaceParameters(sourceParameter, destinationParameter);
5257
}
5358
}
@@ -263,36 +268,34 @@ public static Expression ForEach(ParameterExpression loopVar, Expression collect
263268
static Expression ForEachArrayItem(ParameterExpression loopVar, Expression array, Expression loopContent)
264269
{
265270
var breakLabel = Label("LoopBreak");
266-
var index = Variable(typeof(int), "sourceArrayIndex");
267-
var loop = Block(new[] { index, loopVar },
268-
Assign(index, Zero),
271+
var loop = Block(new[] { Index, loopVar },
272+
ResetIndex,
269273
Loop(
270274
IfThenElse(
271-
LessThan(index, ArrayLength(array)),
272-
Block(Assign(loopVar, ArrayAccess(array, index)), loopContent, PostIncrementAssign(index)),
275+
LessThan(Index, ArrayLength(array)),
276+
Block(Assign(loopVar, ArrayAccess(array, Index)), loopContent, IncrementIndex),
273277
Break(breakLabel)
274278
),
275279
breakLabel));
276280
return loop;
277281
}
278-
static Expression Using(Expression disposable, Expression body)
282+
static Expression Using(Expression target, Expression body)
279283
{
280-
Expression disposeCall;
281-
if (typeof(IDisposable).IsAssignableFrom(disposable.Type))
284+
Expression finallyDispose;
285+
if (typeof(IDisposable).IsAssignableFrom(target.Type))
282286
{
283-
disposeCall = Expression.Call(disposable, DisposeMethod);
287+
finallyDispose = Expression.Call(target, DisposeMethod);
284288
}
285289
else
286290
{
287-
if (disposable.Type.IsValueType)
291+
if (target.Type.IsValueType)
288292
{
289293
return body;
290294
}
291-
var disposableVariable = Variable(typeof(IDisposable), "disposableVariable");
292-
var assignDisposable = Assign(disposableVariable, TypeAs(disposable, typeof(IDisposable)));
293-
disposeCall = Block(new[] { disposableVariable }, assignDisposable, IfNullElse(disposableVariable, Empty, Expression.Call(disposableVariable, DisposeMethod)));
295+
var assignDisposable = Assign(Disposable, TypeAs(target, typeof(IDisposable)));
296+
finallyDispose = Block(DisposableArray, assignDisposable, DisposeCall);
294297
}
295-
return TryFinally(body, disposeCall);
298+
return TryFinally(body, finallyDispose);
296299
}
297300
}
298301
// Expression.Property(string) is inefficient because it does a case insensitive match
@@ -325,9 +328,9 @@ public static Expression NullCheck(this Expression expression, Type destinationT
325328
{
326329
return expression;
327330
}
328-
var returnType = (destinationType != null && destinationType != expression.Type && Nullable.GetUnderlyingType(destinationType) == expression.Type) ?
331+
var returnType = (destinationType != null && destinationType != expression.Type && Nullable.GetUnderlyingType(destinationType) == expression.Type) ?
329332
destinationType : expression.Type;
330-
var defaultReturn = defaultValue?.Type == returnType ? defaultValue : Default(returnType);
333+
var defaultReturn = (defaultValue is { NodeType: ExpressionType.Default } && defaultValue.Type == returnType) ? defaultValue : Default(returnType);
331334
ParameterExpression[] variables = null;
332335
var name = parameter.Name;
333336
int index = 0;
@@ -351,9 +354,9 @@ static Expression UpdateTarget(Expression sourceExpression, Expression newTarget
351354
sourceExpression switch
352355
{
353356
MemberExpression memberExpression => memberExpression.Update(newTarget),
354-
MethodCallExpression { Method: { IsStatic: true }, Arguments: var args } methodCall when args[0] != newTarget =>
355-
methodCall.Update(null, new[] { newTarget }.Concat(args.Skip(1))),
356-
MethodCallExpression { Method: { IsStatic: false } } methodCall => methodCall.Update(newTarget, methodCall.Arguments),
357+
MethodCallExpression { Method.IsStatic: true, Arguments: var args } methodCall when args[0] != newTarget =>
358+
methodCall.Update(null, args.Skip(1).Prepend(newTarget)),
359+
MethodCallExpression { Method.IsStatic: false } methodCall => methodCall.Update(newTarget, methodCall.Arguments),
357360
_ => sourceExpression,
358361
};
359362
}

src/AutoMapper/Execution/ProxyGenerator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ public static class ProxyGenerator
1919
private static readonly LockingConcurrentDictionary<TypeDescription, Type> ProxyTypes = new LockingConcurrentDictionary<TypeDescription, Type>(EmitProxy);
2020
private static ModuleBuilder CreateProxyModule()
2121
{
22-
var builder = AssemblyBuilder.DefineDynamicAssembly(typeof(Mapper).Assembly.GetName(), AssemblyBuilderAccess.Run);
23-
return builder.DefineDynamicModule("AutoMapper.Proxies.emit");
22+
var assemblyName = typeof(Mapper).Assembly.GetName();
23+
assemblyName.Name = "AutoMapper.Proxies.emit";
24+
var builder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
25+
return builder.DefineDynamicModule(assemblyName.Name);
2426
}
2527
private static Type EmitProxy(TypeDescription typeDescription)
2628
{

src/AutoMapper/Execution/TypeMapPlanBuilder.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ private static void CheckForCycles(IGlobalConfiguration configurationProvider, T
8787
{
8888
if (typeMap.DestinationTypeOverride != null)
8989
{
90-
CheckForCycles(configurationProvider, configurationProvider.GetIncludedTypeMap(typeMap.GetAsPair()), typeMapsPath);
90+
CheckForCycles(configurationProvider, configurationProvider.GetIncludedTypeMap(typeMap.AsPair()), typeMapsPath);
9191
return;
9292
}
9393
typeMapsPath.Add(typeMap);
@@ -179,13 +179,12 @@ private Expression CreateAssignmentFunc(Expression createDestination)
179179
{
180180
actions.Add(beforeMapAction.ReplaceParameters(Source, _destination, ContextParameter));
181181
}
182-
var isConstructorMapping = _typeMap.ConstructorMapping;
183182
foreach (var propertyMap in _typeMap.PropertyMaps)
184183
{
185184
if (propertyMap.CanResolveValue)
186185
{
187186
var property = TryPropertyMap(propertyMap);
188-
if (isConstructorMapping && _typeMap.ConstructorParameterMatches(propertyMap.DestinationName))
187+
if (_typeMap.ConstructorParameterMatches(propertyMap.DestinationName))
189188
{
190189
property = _initialDestination.IfNullElse(Default(property.Type), property);
191190
}
@@ -219,7 +218,7 @@ private Expression TryPathMap(PathMap pathMap)
219218
return TryMemberMap(pathMap, pathMapExpression);
220219
static Expression CreateInnerObjects(Expression destination)
221220
{
222-
return Block(destination.GetMemberExpressions().Select(NullCheck).Concat(new[] { ExpressionBuilder.Empty }));
221+
return Block(destination.GetMemberExpressions().Select(NullCheck).Append(ExpressionBuilder.Empty));
223222
static Expression NullCheck(MemberExpression memberExpression)
224223
{
225224
var setter = GetSetter(memberExpression);
@@ -274,7 +273,7 @@ private Expression ConstructorMapping(ConstructorMap constructorMap)
274273
var ctorArgs = constructorMap.CtorParams.Select(CreateConstructorParameterExpression);
275274
var variables = constructorMap.Ctor.GetParameters().Select(parameter => Variable(parameter.ParameterType, parameter.Name)).ToArray();
276275
var body = variables.Zip(ctorArgs, (variable, expression) => (Expression)Assign(variable, ToType(expression, variable.Type)))
277-
.Concat(new[] { CheckReferencesCache(New(constructorMap.Ctor, variables)) });
276+
.Append(CheckReferencesCache(New(constructorMap.Ctor, variables)));
278277
return Block(variables, body);
279278
}
280279
private Expression CreateConstructorParameterExpression(ConstructorParameterMap ctorParamMap)
@@ -396,7 +395,7 @@ private Expression BuildValueResolverFunc(MemberMap memberMap, Expression destVa
396395
{ ValueResolverConfig: { } } => BuildResolveCall(memberMap, customSource, destValueExpr),
397396
{ CustomMapFunction: LambdaExpression function } => function.ConvertReplaceParameters(customSource, _destination, destValueExpr, ContextParameter),
398397
{ CustomMapExpression: LambdaExpression mapFrom } => CustomMapExpression(mapFrom.ReplaceParameters(customSource), destinationPropertyType, destValueExpr),
399-
{ SourceMembers: { Length: > 0 } } => memberMap.ChainSourceMembers(customSource, destinationPropertyType, destValueExpr),
398+
{ SourceMembers.Length: > 0 } => memberMap.ChainSourceMembers(customSource, destinationPropertyType, destValueExpr),
400399
_ => destValueExpr
401400
};
402401
if (memberMap.NullSubstitute != null)
@@ -444,7 +443,7 @@ private Expression BuildResolveCall(MemberMap memberMap, Expression source, Expr
444443
}
445444
var parameters = new[] { source, _destination, sourceMember, destValueExpr }.Where(p => p != null)
446445
.Zip(iResolverType.GenericTypeArguments, ToType)
447-
.Concat(new[] { ContextParameter })
446+
.Append(ContextParameter)
448447
.ToArray();
449448
return Call(ToType(resolverInstance, iResolverType), "Resolve", parameters);
450449
}

src/AutoMapper/Internal/InternalApi.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ public interface IGlobalConfiguration : IConfigurationProvider
153153
TypeMap GetIncludedTypeMap(TypePair typePair);
154154
TypeMap GetIncludedTypeMap(Type sourceType, Type destinationType);
155155
TypeMap[] GetIncludedTypeMaps(IReadOnlyCollection<TypePair> includedTypes);
156+
void Seal(TypeMap typeMap);
156157
}
157158
[EditorBrowsable(EditorBrowsableState.Never)]
158159
public interface IProfileExpressionInternal : IProfileExpression

src/AutoMapper/Internal/ReflectionHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace AutoMapper.Internal
99
[EditorBrowsable(EditorBrowsableState.Never)]
1010
public static class ReflectionHelper
1111
{
12+
public static Type FirstParameterType(this MethodBase method) => method.GetParameters()[0].ParameterType;
1213
public static Type GetElementType(Type type) => type.IsArray ? type.GetElementType() : GetEnumerableElementType(type);
1314
public static Type GetEnumerableElementType(Type type) => type.GetIEnumerableType()?.GenericTypeArguments[0] ?? typeof(object);
1415
public static TypeMap[] GetIncludedTypeMaps(this IGlobalConfiguration configuration, TypeMap typeMap) =>

0 commit comments

Comments
 (0)