diff --git a/Setup.ps1 b/Setup.ps1 index a20035ad42..ebbf3e9a29 100644 --- a/Setup.ps1 +++ b/Setup.ps1 @@ -27,6 +27,7 @@ Import-Module BitsTransfer Start-BitsTransfer -Source https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi -Destination SqlLocalDB.msi Write-Host "Installing" Start-Process -FilePath "SqlLocalDB.msi" -Wait -ArgumentList "/qn", "/norestart", "/l*v SqlLocalDBInstall.log", "IACCEPTSQLLOCALDBLICENSETERMS=YES"; +<# Write-Host "Checking" sqlcmd -l 60 -S "(localdb)\MSSQLLocalDB" -Q "SELECT @@VERSION;" - +#> diff --git a/src/AutoMapper/Configuration/ConfigurationValidator.cs b/src/AutoMapper/Configuration/ConfigurationValidator.cs index 6b4c616bce..43f701a72e 100644 --- a/src/AutoMapper/Configuration/ConfigurationValidator.cs +++ b/src/AutoMapper/Configuration/ConfigurationValidator.cs @@ -33,7 +33,7 @@ public void AssertConfigurationExpressionIsValid(IEnumerable typeMaps) { if (!_expression.AllowAdditiveTypeMapCreation) { - var duplicateTypeMapConfigs = _expression.Profiles.Concat(new[] { (Profile)_expression }) + var duplicateTypeMapConfigs = _expression.Profiles.Append((Profile)_expression) .SelectMany(p => p.TypeMapConfigs, (profile, typeMap) => (profile, typeMap)) .GroupBy(x => x.typeMap.Types) .Where(g => g.Count() > 1) diff --git a/src/AutoMapper/Configuration/MapperConfiguration.cs b/src/AutoMapper/Configuration/MapperConfiguration.cs index d44b28f705..c5def1e112 100644 --- a/src/AutoMapper/Configuration/MapperConfiguration.cs +++ b/src/AutoMapper/Configuration/MapperConfiguration.cs @@ -55,6 +55,7 @@ public class MapperConfiguration : IGlobalConfiguration private readonly IObjectMapper[] _mappers; private readonly Dictionary _configuredMaps; private readonly Dictionary _resolvedMaps; + private HashSet _typeMapsPath = new(); private readonly LockingConcurrentDictionary _runtimeMaps; private readonly ProjectionBuilder _projectionBuilder; private readonly LockingConcurrentDictionary _executionPlans; @@ -109,7 +110,59 @@ public MapperConfiguration(MapperConfigurationExpression configurationExpression { profile.Clear(); } + _typeMapsPath = null; _sealed = true; + return; + void Seal() + { + foreach (var profile in Profiles) + { + profile.Register(this); + } + foreach (var profile in Profiles) + { + profile.Configure(this); + } + IGlobalConfiguration globalConfiguration = this; + var derivedMaps = new List(); + foreach (var typeMap in _configuredMaps.Values) + { + if (typeMap.DestinationTypeOverride != null) + { + var derivedMap = globalConfiguration.GetIncludedTypeMap(typeMap.AsPair()); + _resolvedMaps[typeMap.Types] = derivedMap; + } + else + { + _resolvedMaps[typeMap.Types] = typeMap; + } + derivedMaps.Clear(); + GetDerivedTypeMaps(typeMap, derivedMaps); + foreach (var derivedMap in derivedMaps) + { + var includedPair = new TypePair(derivedMap.SourceType, typeMap.DestinationType); + _resolvedMaps.TryAdd(includedPair, derivedMap); + } + } + foreach (var typeMap in _configuredMaps.Values) + { + typeMap.Seal(this, _typeMapsPath); + } + _features.Seal(this); + } + void GetDerivedTypeMaps(TypeMap typeMap, List typeMaps) + { + foreach (var derivedMap in this.Internal().GetIncludedTypeMaps(typeMap)) + { + typeMaps.Add(derivedMap); + GetDerivedTypeMaps(derivedMap, typeMaps); + } + } + Delegate CompileExecutionPlan(MapRequest mapRequest) + { + var executionPlan = ((IGlobalConfiguration)this).BuildExecutionPlan(mapRequest); + return executionPlan.Compile(); // breakpoint here to inspect all execution plans + } } public MapperConfiguration(Action configure) : this(Build(configure)) @@ -139,15 +192,62 @@ LambdaExpression IGlobalConfiguration.BuildExecutionPlan(in MapRequest mapReques } var mapperToUse = FindMapper(mapRequest.RuntimeTypes); return GenerateObjectMapperExpression(mapRequest, mapperToUse); + static LambdaExpression GenerateTypeMapExpression(TypePair requestedTypes, TypeMap typeMap) + { + typeMap.CheckProjection(); + if (requestedTypes == typeMap.Types) + { + return typeMap.MapExpression; + } + var mapDestinationType = typeMap.DestinationType; + var requestedDestinationType = requestedTypes.DestinationType; + var source = Parameter(requestedTypes.SourceType, "source"); + var destination = Parameter(requestedDestinationType, "typeMapDestination"); + var checkNullValueTypeDest = CheckNullValueType(destination, mapDestinationType); + return + Lambda( + ToType( + Invoke(typeMap.MapExpression, ToType(source, typeMap.SourceType), ToType(checkNullValueTypeDest, mapDestinationType), ContextParameter), + requestedDestinationType), + source, destination, ContextParameter); + } + static Expression CheckNullValueType(Expression expression, Type runtimeType) => + !expression.Type.IsValueType && runtimeType.IsValueType ? Coalesce(expression, Default(runtimeType)) : expression; + LambdaExpression GenerateObjectMapperExpression(in MapRequest mapRequest, IObjectMapper mapperToUse) + { + var source = Parameter(mapRequest.RequestedTypes.SourceType, "source"); + var destination = Parameter(mapRequest.RequestedTypes.DestinationType, "mapperDestination"); + var runtimeDestinationType = mapRequest.RuntimeTypes.DestinationType; + Expression fullExpression; + if (mapperToUse == null) + { + var exception = new AutoMapperMappingException("Missing type map configuration or unsupported mapping.", null, mapRequest.RuntimeTypes) + { + MemberMap = mapRequest.MemberMap + }; + fullExpression = Throw(Constant(exception), runtimeDestinationType); + } + else + { + var checkNullValueTypeDest = CheckNullValueType(destination, runtimeDestinationType); + var map = mapperToUse.MapExpression(this, Configuration, mapRequest.MemberMap, + ToType(source, mapRequest.RuntimeTypes.SourceType), + ToType(checkNullValueTypeDest, runtimeDestinationType)); + var newException = Call(MappingError, ExceptionParameter, Constant(mapRequest)); + var throwExpression = Throw(newException, runtimeDestinationType); + fullExpression = TryCatch(ToType(map, runtimeDestinationType), Catch(ExceptionParameter, throwExpression)); + } + var profileMap = mapRequest.MemberMap?.Profile ?? Configuration; + var nullCheckSource = NullCheckSource(profileMap, source, destination, fullExpression, mapRequest.MemberMap); + return Lambda(nullCheckSource, source, destination, ContextParameter); + } } - - private static MapperConfigurationExpression Build(Action configure) + static MapperConfigurationExpression Build(Action configure) { var expr = new MapperConfigurationExpression(); configure(expr); return expr; } - IProjectionBuilder IGlobalConfiguration.ProjectionBuilder => _projectionBuilder; Func IGlobalConfiguration.ServiceCtor => _serviceCtor; bool IGlobalConfiguration.EnableNullPropagationForQueryMapping => _enableNullPropagationForQueryMapping; @@ -159,15 +259,7 @@ private static MapperConfigurationExpression Build(Action IGlobalConfiguration.Features => _features; Func IGlobalConfiguration.GetExecutionPlan(in MapRequest mapRequest) => (Func)GetExecutionPlan(mapRequest); - private Delegate GetExecutionPlan(in MapRequest mapRequest) => _executionPlans.GetOrAdd(mapRequest); - - private Delegate CompileExecutionPlan(MapRequest mapRequest) - { - var executionPlan = ((IGlobalConfiguration)this).BuildExecutionPlan(mapRequest); - return executionPlan.Compile(); // breakpoint here to inspect all execution plans - } - TypeMap IGlobalConfiguration.ResolveAssociatedTypeMap(TypePair types) { var typeMap = ResolveTypeMap(types); @@ -181,56 +273,6 @@ TypeMap IGlobalConfiguration.ResolveAssociatedTypeMap(TypePair types) } return null; } - - private static LambdaExpression GenerateTypeMapExpression(TypePair requestedTypes, TypeMap typeMap) - { - typeMap.CheckProjection(); - if (requestedTypes == typeMap.Types) - { - return typeMap.MapExpression; - } - var mapDestinationType = typeMap.DestinationType; - var requestedDestinationType = requestedTypes.DestinationType; - var source = Parameter(requestedTypes.SourceType, "source"); - var destination = Parameter(requestedDestinationType, "typeMapDestination"); - var checkNullValueTypeDest = CheckNullValueType(destination, mapDestinationType); - return - Lambda( - ToType( - Invoke(typeMap.MapExpression, ToType(source, typeMap.SourceType), ToType(checkNullValueTypeDest, mapDestinationType), ContextParameter), - requestedDestinationType), - source, destination, ContextParameter); - } - private static Expression CheckNullValueType(Expression expression, Type runtimeType) => - !expression.Type.IsValueType && runtimeType.IsValueType ? Coalesce(expression, Default(runtimeType)) : expression; - private LambdaExpression GenerateObjectMapperExpression(in MapRequest mapRequest, IObjectMapper mapperToUse) - { - var source = Parameter(mapRequest.RequestedTypes.SourceType, "source"); - var destination = Parameter(mapRequest.RequestedTypes.DestinationType, "mapperDestination"); - var runtimeDestinationType = mapRequest.RuntimeTypes.DestinationType; - Expression fullExpression; - if (mapperToUse == null) - { - var exception = new AutoMapperMappingException("Missing type map configuration or unsupported mapping.", null, mapRequest.RuntimeTypes) - { - MemberMap = mapRequest.MemberMap - }; - fullExpression = Throw(Constant(exception), runtimeDestinationType); - } - else - { - var checkNullValueTypeDest = CheckNullValueType(destination, runtimeDestinationType); - var map = mapperToUse.MapExpression(this, Configuration, mapRequest.MemberMap, - ToType(source, mapRequest.RuntimeTypes.SourceType), - ToType(checkNullValueTypeDest, runtimeDestinationType)); - var newException = Call(MappingError, ExceptionParameter, Constant(mapRequest)); - var throwExpression = Throw(newException, runtimeDestinationType); - fullExpression = TryCatch(ToType(map, runtimeDestinationType), Catch(ExceptionParameter, throwExpression)); - } - var profileMap = mapRequest.MemberMap?.Profile ?? Configuration; - var nullCheckSource = NullCheckSource(profileMap, source, destination, fullExpression, mapRequest.MemberMap); - return Lambda(nullCheckSource, source, destination, ContextParameter); - } public static AutoMapperMappingException GetMappingError(Exception innerException, in MapRequest mapRequest) => new("Error mapping types.", innerException, mapRequest.RuntimeTypes) { MemberMap = mapRequest.MemberMap }; IReadOnlyCollection IGlobalConfiguration.GetAllTypeMaps() => _configuredMaps.Values; @@ -263,6 +305,10 @@ TypeMap ResolveTypeMap(TypePair typePair) { typeMap = GetTypeMap(typePair); _resolvedMaps.Add(typePair, typeMap); + if (typeMap != null && typeMap.MapExpression == null) + { + typeMap.Seal(this, _typeMapsPath); + } } return typeMap; } @@ -296,81 +342,78 @@ private TypeMap GetTypeMap(TypePair initialTypes) } } return null; - } - static List GetTypeInheritance(Type type) - { - var interfaces = type.GetInterfaces(); - var lastIndex = interfaces.Length - 1; - var types = new List(interfaces.Length + 2) { type }; - Type baseType = type; - while ((baseType = baseType.BaseType) != null) + static List GetTypeInheritance(Type type) { - types.Add(baseType); - foreach (var interfaceType in baseType.GetInterfaces()) + var interfaces = type.GetInterfaces(); + var lastIndex = interfaces.Length - 1; + var types = new List(interfaces.Length + 2) { type }; + Type baseType = type; + while ((baseType = baseType.BaseType) != null) { - var interfaceIndex = Array.LastIndexOf(interfaces, interfaceType); - if (interfaceIndex != lastIndex) + types.Add(baseType); + foreach (var interfaceType in baseType.GetInterfaces()) { - interfaces[interfaceIndex] = interfaces[lastIndex]; - interfaces[lastIndex] = interfaceType; + var interfaceIndex = Array.LastIndexOf(interfaces, interfaceType); + if (interfaceIndex != lastIndex) + { + interfaces[interfaceIndex] = interfaces[lastIndex]; + interfaces[lastIndex] = interfaceType; + } } } - } - foreach (var interfaceType in interfaces) - { - types.Add(interfaceType); - } - return types; - } - IEnumerable IGlobalConfiguration.GetMappers() => _mappers; - private void Seal() - { - foreach (var profile in Profiles) - { - profile.Register(this); - } - foreach (var profile in Profiles) - { - profile.Configure(this); - } - IGlobalConfiguration globalConfiguration = this; - var derivedMaps = new List(); - foreach (var typeMap in _configuredMaps.Values) - { - if (typeMap.DestinationTypeOverride != null) + foreach (var interfaceType in interfaces) { - var derivedMap = globalConfiguration.GetIncludedTypeMap(typeMap.GetAsPair()); - _resolvedMaps[typeMap.Types] = derivedMap; + types.Add(interfaceType); } - else + return types; + } + TypeMap FindClosedGenericTypeMapFor(TypePair typePair) + { + if (!_hasOpenMaps || !typePair.IsConstructedGenericType) { - _resolvedMaps[typeMap.Types] = typeMap; + return null; } - derivedMaps.Clear(); - GetDerivedTypeMaps(typeMap,derivedMaps); - foreach (var derivedMap in derivedMaps) + return FindClosedGenericMap(typePair); + TypeMap FindClosedGenericMap(TypePair typePair) { - var includedPair = new TypePair(derivedMap.SourceType, typeMap.DestinationType); - _resolvedMaps.TryAdd(includedPair, derivedMap); + var genericTypePair = typePair.GetTypeDefinitionIfGeneric(); + var userMap = + FindTypeMapFor(genericTypePair.SourceType, typePair.DestinationType) ?? + FindTypeMapFor(typePair.SourceType, genericTypePair.DestinationType) ?? + FindTypeMapFor(genericTypePair); + ITypeMapConfiguration genericMapConfig; + ProfileMap profile; + TypeMap cachedMap; + TypePair closedTypes; + if (userMap != null && userMap.DestinationTypeOverride == null) + { + genericMapConfig = userMap.Profile.GetGenericMap(userMap.Types); + profile = userMap.Profile; + cachedMap = null; + closedTypes = typePair; + } + else + { + var foundGenericMap = _resolvedMaps.TryGetValue(genericTypePair, out cachedMap) && cachedMap.Types.ContainsGenericParameters; + if (!foundGenericMap) + { + return cachedMap; + } + genericMapConfig = cachedMap.Profile.GetGenericMap(cachedMap.Types); + profile = cachedMap.Profile; + closedTypes = cachedMap.Types.CloseGenericTypes(typePair); + } + if (genericMapConfig == null) + { + return null; + } + var typeMap = profile.CreateClosedGenericTypeMap(genericMapConfig, closedTypes, this); + cachedMap?.CopyInheritedMapsTo(typeMap); + return typeMap; } } - var typeMapsPath = new HashSet(); - foreach (var typeMap in _configuredMaps.Values) - { - typeMap.Seal(this, typeMapsPath); - } - _features.Seal(this); - } - - private void GetDerivedTypeMaps(TypeMap typeMap, List typeMaps) - { - foreach (var derivedMap in this.Internal().GetIncludedTypeMaps(typeMap)) - { - typeMaps.Add(derivedMap); - GetDerivedTypeMaps(derivedMap, typeMaps); - } } - + IEnumerable IGlobalConfiguration.GetMappers() => _mappers; TypeMap[] IGlobalConfiguration.GetIncludedTypeMaps(IReadOnlyCollection includedTypes) { if (includedTypes.Count == 0) @@ -406,51 +449,6 @@ TypeMap GetIncludedTypeMap(TypePair pair) return typeMap; } } - private TypeMap FindClosedGenericTypeMapFor(TypePair typePair) - { - if (!_hasOpenMaps || !typePair.IsConstructedGenericType) - { - return null; - } - return FindClosedGenericMap(typePair); - TypeMap FindClosedGenericMap(TypePair typePair) - { - var genericTypePair = typePair.GetTypeDefinitionIfGeneric(); - var userMap = - FindTypeMapFor(genericTypePair.SourceType, typePair.DestinationType) ?? - FindTypeMapFor(typePair.SourceType, genericTypePair.DestinationType) ?? - FindTypeMapFor(genericTypePair); - ITypeMapConfiguration genericMapConfig; - ProfileMap profile; - TypeMap cachedMap; - TypePair closedTypes; - if (userMap != null && userMap.DestinationTypeOverride == null) - { - genericMapConfig = userMap.Profile.GetGenericMap(userMap.Types); - profile = userMap.Profile; - cachedMap = null; - closedTypes = typePair; - } - else - { - var foundGenericMap = _resolvedMaps.TryGetValue(genericTypePair, out cachedMap) && cachedMap.Types.ContainsGenericParameters; - if (!foundGenericMap) - { - return cachedMap; - } - genericMapConfig = cachedMap.Profile.GetGenericMap(cachedMap.Types); - profile = cachedMap.Profile; - closedTypes = cachedMap.Types.CloseGenericTypes(typePair); - } - if (genericMapConfig == null) - { - return null; - } - var typeMap = profile.CreateClosedGenericTypeMap(genericMapConfig, closedTypes, this); - cachedMap?.CopyInheritedMapsTo(typeMap); - return typeMap; - } - } IObjectMapper IGlobalConfiguration.FindMapper(TypePair types) => FindMapper(types); IObjectMapper FindMapper(TypePair types) { @@ -474,5 +472,6 @@ void IGlobalConfiguration.AssertConfigurationIsValid(string profileName) _validator.AssertConfigurationIsValid(_configuredMaps.Values.Where(typeMap => typeMap.Profile.Name == profileName)); } void IGlobalConfiguration.AssertConfigurationIsValid() => this.Internal().AssertConfigurationIsValid(typeof(TProfile).FullName); + void IGlobalConfiguration.Seal(TypeMap typeMap) => typeMap.Seal(this, _typeMapsPath); } } \ No newline at end of file diff --git a/src/AutoMapper/Configuration/MappingExpressionBase.cs b/src/AutoMapper/Configuration/MappingExpressionBase.cs index b52f9c2f34..11d05fd780 100644 --- a/src/AutoMapper/Configuration/MappingExpressionBase.cs +++ b/src/AutoMapper/Configuration/MappingExpressionBase.cs @@ -20,6 +20,7 @@ public interface ITypeMapConfiguration TypePair Types { get; } ITypeMapConfiguration ReverseTypeMap { get; } TypeMap TypeMap { get; } + bool HasTypeConverter { get; } } public abstract class MappingExpressionBase : ITypeMapConfiguration { @@ -42,6 +43,7 @@ protected MappingExpressionBase(MemberList memberList, TypePair types) protected bool Projection { get; set; } public TypePair Types => _types; public bool IsReverseMap { get; set; } + public bool HasTypeConverter { get; protected set; } public TypeMap TypeMap { get; private set; } public Type SourceType => _types.SourceType; public Type DestinationType => _types.DestinationType; @@ -451,11 +453,15 @@ public TMappingExpression ConstructUsing(Func TypeMapActions.Add(tm => tm.TypeConverterType = typeConverterType); + public void ConvertUsing(Type typeConverterType) + { + HasTypeConverter = true; + TypeMapActions.Add(tm => tm.TypeConverterType = typeConverterType); + } public void ConvertUsing(Func mappingFunction) { + HasTypeConverter = true; TypeMapActions.Add(tm => { Expression> expr = @@ -467,6 +473,7 @@ public void ConvertUsing(Func mappingFuncti public void ConvertUsing(Func mappingFunction) { + HasTypeConverter = true; TypeMapActions.Add(tm => { Expression> expr = @@ -476,13 +483,11 @@ public void ConvertUsing(Func converter) - { - ConvertUsing(converter.Convert); - } + public void ConvertUsing(ITypeConverter converter) => ConvertUsing(converter.Convert); public void ConvertUsing() where TTypeConverter : ITypeConverter { + HasTypeConverter = true; TypeMapActions.Add(tm => tm.TypeConverterType = typeof(TTypeConverter)); } @@ -517,8 +522,11 @@ public TMappingExpression IgnoreAllSourcePropertiesWithAnInaccessibleSetter() private static IEnumerable PropertiesWithAnInaccessibleSetter(Type type) => type.GetRuntimeProperties().Where(p => p.GetSetMethod() == null); - public void ConvertUsing(Expression> mappingFunction) => + public void ConvertUsing(Expression> mappingFunction) + { + HasTypeConverter = true; TypeMapActions.Add(tm => tm.CustomMapExpression = mappingFunction); + } public TMappingExpression AsProxy() { diff --git a/src/AutoMapper/Execution/ExpressionBuilder.cs b/src/AutoMapper/Execution/ExpressionBuilder.cs index f00fcbd89d..29910d79c8 100644 --- a/src/AutoMapper/Execution/ExpressionBuilder.cs +++ b/src/AutoMapper/Execution/ExpressionBuilder.cs @@ -16,7 +16,6 @@ public static class ExpressionBuilder { public static readonly MethodInfo ObjectToString = typeof(object).GetMethod(nameof(object.ToString)); private static readonly MethodInfo DisposeMethod = typeof(IDisposable).GetMethod(nameof(IDisposable.Dispose)); - public static readonly Expression False = Constant(false, typeof(bool)); public static readonly Expression True = Constant(true, typeof(bool)); public static readonly Expression Null = Constant(null, typeof(object)); public static readonly Expression Empty = Empty(); @@ -34,6 +33,12 @@ public static class ExpressionBuilder private static readonly MethodInfo CheckContextMethod = typeof(ResolutionContext).GetStaticMethod(nameof(ResolutionContext.CheckContext)); private static readonly MethodInfo ContextMapMethod = typeof(ResolutionContext).GetInstanceMethod(nameof(ResolutionContext.MapInternal)); private static readonly MethodInfo ArrayEmptyMethod = typeof(Array).GetStaticMethod(nameof(Array.Empty)); + private static readonly ParameterExpression Disposable = Variable(typeof(IDisposable), "disposableEnumerator"); + private static readonly ParameterExpression[] DisposableArray = new[] { Disposable }; + private static readonly Expression DisposeCall = IfNullElse(Disposable, Empty, Expression.Call(Disposable, DisposeMethod)); + private static readonly ParameterExpression Index = Variable(typeof(int), "sourceArrayIndex"); + private static readonly BinaryExpression ResetIndex = Assign(Index, Zero); + private static readonly UnaryExpression IncrementIndex = PostIncrementAssign(Index); public static Expression MapExpression(this IGlobalConfiguration configurationProvider, ProfileMap profileMap, TypePair typePair, Expression sourceParameter, MemberMap propertyMap = null, Expression destinationParameter = null) @@ -47,7 +52,7 @@ public static Expression MapExpression(this IGlobalConfiguration configurationPr hasTypeConverter = typeMap.HasTypeConverter; if (!typeMap.HasDerivedTypesToInclude) { - typeMap.Seal(configurationProvider); + configurationProvider.Seal(typeMap); mapExpression = typeMap.MapExpression?.ConvertReplaceParameters(sourceParameter, destinationParameter); } } @@ -263,36 +268,34 @@ public static Expression ForEach(ParameterExpression loopVar, Expression collect static Expression ForEachArrayItem(ParameterExpression loopVar, Expression array, Expression loopContent) { var breakLabel = Label("LoopBreak"); - var index = Variable(typeof(int), "sourceArrayIndex"); - var loop = Block(new[] { index, loopVar }, - Assign(index, Zero), + var loop = Block(new[] { Index, loopVar }, + ResetIndex, Loop( IfThenElse( - LessThan(index, ArrayLength(array)), - Block(Assign(loopVar, ArrayAccess(array, index)), loopContent, PostIncrementAssign(index)), + LessThan(Index, ArrayLength(array)), + Block(Assign(loopVar, ArrayAccess(array, Index)), loopContent, IncrementIndex), Break(breakLabel) ), breakLabel)); return loop; } - static Expression Using(Expression disposable, Expression body) + static Expression Using(Expression target, Expression body) { - Expression disposeCall; - if (typeof(IDisposable).IsAssignableFrom(disposable.Type)) + Expression finallyDispose; + if (typeof(IDisposable).IsAssignableFrom(target.Type)) { - disposeCall = Expression.Call(disposable, DisposeMethod); + finallyDispose = Expression.Call(target, DisposeMethod); } else { - if (disposable.Type.IsValueType) + if (target.Type.IsValueType) { return body; } - var disposableVariable = Variable(typeof(IDisposable), "disposableVariable"); - var assignDisposable = Assign(disposableVariable, TypeAs(disposable, typeof(IDisposable))); - disposeCall = Block(new[] { disposableVariable }, assignDisposable, IfNullElse(disposableVariable, Empty, Expression.Call(disposableVariable, DisposeMethod))); + var assignDisposable = Assign(Disposable, TypeAs(target, typeof(IDisposable))); + finallyDispose = Block(DisposableArray, assignDisposable, DisposeCall); } - return TryFinally(body, disposeCall); + return TryFinally(body, finallyDispose); } } // 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 { return expression; } - var returnType = (destinationType != null && destinationType != expression.Type && Nullable.GetUnderlyingType(destinationType) == expression.Type) ? + var returnType = (destinationType != null && destinationType != expression.Type && Nullable.GetUnderlyingType(destinationType) == expression.Type) ? destinationType : expression.Type; - var defaultReturn = defaultValue?.Type == returnType ? defaultValue : Default(returnType); + var defaultReturn = (defaultValue is { NodeType: ExpressionType.Default } && defaultValue.Type == returnType) ? defaultValue : Default(returnType); ParameterExpression[] variables = null; var name = parameter.Name; int index = 0; @@ -351,9 +354,9 @@ static Expression UpdateTarget(Expression sourceExpression, Expression newTarget sourceExpression switch { MemberExpression memberExpression => memberExpression.Update(newTarget), - MethodCallExpression { Method: { IsStatic: true }, Arguments: var args } methodCall when args[0] != newTarget => - methodCall.Update(null, new[] { newTarget }.Concat(args.Skip(1))), - MethodCallExpression { Method: { IsStatic: false } } methodCall => methodCall.Update(newTarget, methodCall.Arguments), + MethodCallExpression { Method.IsStatic: true, Arguments: var args } methodCall when args[0] != newTarget => + methodCall.Update(null, args.Skip(1).Prepend(newTarget)), + MethodCallExpression { Method.IsStatic: false } methodCall => methodCall.Update(newTarget, methodCall.Arguments), _ => sourceExpression, }; } diff --git a/src/AutoMapper/Execution/ProxyGenerator.cs b/src/AutoMapper/Execution/ProxyGenerator.cs index 6fd19c2ea2..ceb6b3758c 100644 --- a/src/AutoMapper/Execution/ProxyGenerator.cs +++ b/src/AutoMapper/Execution/ProxyGenerator.cs @@ -19,8 +19,10 @@ public static class ProxyGenerator private static readonly LockingConcurrentDictionary ProxyTypes = new LockingConcurrentDictionary(EmitProxy); private static ModuleBuilder CreateProxyModule() { - var builder = AssemblyBuilder.DefineDynamicAssembly(typeof(Mapper).Assembly.GetName(), AssemblyBuilderAccess.Run); - return builder.DefineDynamicModule("AutoMapper.Proxies.emit"); + var assemblyName = typeof(Mapper).Assembly.GetName(); + assemblyName.Name = "AutoMapper.Proxies.emit"; + var builder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + return builder.DefineDynamicModule(assemblyName.Name); } private static Type EmitProxy(TypeDescription typeDescription) { diff --git a/src/AutoMapper/Execution/TypeMapPlanBuilder.cs b/src/AutoMapper/Execution/TypeMapPlanBuilder.cs index dc6551c978..0342540dd8 100644 --- a/src/AutoMapper/Execution/TypeMapPlanBuilder.cs +++ b/src/AutoMapper/Execution/TypeMapPlanBuilder.cs @@ -87,7 +87,7 @@ private static void CheckForCycles(IGlobalConfiguration configurationProvider, T { if (typeMap.DestinationTypeOverride != null) { - CheckForCycles(configurationProvider, configurationProvider.GetIncludedTypeMap(typeMap.GetAsPair()), typeMapsPath); + CheckForCycles(configurationProvider, configurationProvider.GetIncludedTypeMap(typeMap.AsPair()), typeMapsPath); return; } typeMapsPath.Add(typeMap); @@ -179,13 +179,12 @@ private Expression CreateAssignmentFunc(Expression createDestination) { actions.Add(beforeMapAction.ReplaceParameters(Source, _destination, ContextParameter)); } - var isConstructorMapping = _typeMap.ConstructorMapping; foreach (var propertyMap in _typeMap.PropertyMaps) { if (propertyMap.CanResolveValue) { var property = TryPropertyMap(propertyMap); - if (isConstructorMapping && _typeMap.ConstructorParameterMatches(propertyMap.DestinationName)) + if (_typeMap.ConstructorParameterMatches(propertyMap.DestinationName)) { property = _initialDestination.IfNullElse(Default(property.Type), property); } @@ -219,7 +218,7 @@ private Expression TryPathMap(PathMap pathMap) return TryMemberMap(pathMap, pathMapExpression); static Expression CreateInnerObjects(Expression destination) { - return Block(destination.GetMemberExpressions().Select(NullCheck).Concat(new[] { ExpressionBuilder.Empty })); + return Block(destination.GetMemberExpressions().Select(NullCheck).Append(ExpressionBuilder.Empty)); static Expression NullCheck(MemberExpression memberExpression) { var setter = GetSetter(memberExpression); @@ -274,7 +273,7 @@ private Expression ConstructorMapping(ConstructorMap constructorMap) var ctorArgs = constructorMap.CtorParams.Select(CreateConstructorParameterExpression); var variables = constructorMap.Ctor.GetParameters().Select(parameter => Variable(parameter.ParameterType, parameter.Name)).ToArray(); var body = variables.Zip(ctorArgs, (variable, expression) => (Expression)Assign(variable, ToType(expression, variable.Type))) - .Concat(new[] { CheckReferencesCache(New(constructorMap.Ctor, variables)) }); + .Append(CheckReferencesCache(New(constructorMap.Ctor, variables))); return Block(variables, body); } private Expression CreateConstructorParameterExpression(ConstructorParameterMap ctorParamMap) @@ -396,7 +395,7 @@ private Expression BuildValueResolverFunc(MemberMap memberMap, Expression destVa { ValueResolverConfig: { } } => BuildResolveCall(memberMap, customSource, destValueExpr), { CustomMapFunction: LambdaExpression function } => function.ConvertReplaceParameters(customSource, _destination, destValueExpr, ContextParameter), { CustomMapExpression: LambdaExpression mapFrom } => CustomMapExpression(mapFrom.ReplaceParameters(customSource), destinationPropertyType, destValueExpr), - { SourceMembers: { Length: > 0 } } => memberMap.ChainSourceMembers(customSource, destinationPropertyType, destValueExpr), + { SourceMembers.Length: > 0 } => memberMap.ChainSourceMembers(customSource, destinationPropertyType, destValueExpr), _ => destValueExpr }; if (memberMap.NullSubstitute != null) @@ -444,7 +443,7 @@ private Expression BuildResolveCall(MemberMap memberMap, Expression source, Expr } var parameters = new[] { source, _destination, sourceMember, destValueExpr }.Where(p => p != null) .Zip(iResolverType.GenericTypeArguments, ToType) - .Concat(new[] { ContextParameter }) + .Append(ContextParameter) .ToArray(); return Call(ToType(resolverInstance, iResolverType), "Resolve", parameters); } diff --git a/src/AutoMapper/Internal/InternalApi.cs b/src/AutoMapper/Internal/InternalApi.cs index 10592bb91b..4c5bed334a 100644 --- a/src/AutoMapper/Internal/InternalApi.cs +++ b/src/AutoMapper/Internal/InternalApi.cs @@ -153,6 +153,7 @@ public interface IGlobalConfiguration : IConfigurationProvider TypeMap GetIncludedTypeMap(TypePair typePair); TypeMap GetIncludedTypeMap(Type sourceType, Type destinationType); TypeMap[] GetIncludedTypeMaps(IReadOnlyCollection includedTypes); + void Seal(TypeMap typeMap); } [EditorBrowsable(EditorBrowsableState.Never)] public interface IProfileExpressionInternal : IProfileExpression diff --git a/src/AutoMapper/Internal/ReflectionHelper.cs b/src/AutoMapper/Internal/ReflectionHelper.cs index e1652b6b86..acca515780 100644 --- a/src/AutoMapper/Internal/ReflectionHelper.cs +++ b/src/AutoMapper/Internal/ReflectionHelper.cs @@ -9,6 +9,7 @@ namespace AutoMapper.Internal [EditorBrowsable(EditorBrowsableState.Never)] public static class ReflectionHelper { + public static Type FirstParameterType(this MethodBase method) => method.GetParameters()[0].ParameterType; public static Type GetElementType(Type type) => type.IsArray ? type.GetElementType() : GetEnumerableElementType(type); public static Type GetEnumerableElementType(Type type) => type.GetIEnumerableType()?.GenericTypeArguments[0] ?? typeof(object); public static TypeMap[] GetIncludedTypeMaps(this IGlobalConfiguration configuration, TypeMap typeMap) => diff --git a/src/AutoMapper/Internal/TypeDetails.cs b/src/AutoMapper/Internal/TypeDetails.cs index e864a51a4d..3932912236 100644 --- a/src/AutoMapper/Internal/TypeDetails.cs +++ b/src/AutoMapper/Internal/TypeDetails.cs @@ -1,3 +1,4 @@ +using AutoMapper.Execution; using System; using System.Collections.Generic; using System.ComponentModel; @@ -6,7 +7,6 @@ using System.Reflection; namespace AutoMapper.Internal { - using SourceMembers = Dictionary; /// /// Contains cached reflection information for easy retrieval /// @@ -14,7 +14,7 @@ namespace AutoMapper.Internal [EditorBrowsable(EditorBrowsableState.Never)] public class TypeDetails { - private SourceMembers _nameToMember; + private Dictionary _nameToMember; private ConstructorParameters[] _constructors; private MemberInfo[] _readAccessors; private MemberInfo[] _writeAccessors; @@ -29,43 +29,109 @@ public static IEnumerable GetConstructors(Type type, Prof type.GetDeclaredConstructors().Where(profileMap.ShouldUseConstructor).Select(c => new ConstructorParameters(c)); public MemberInfo GetMember(string name) { - _nameToMember ??= PossibleNames(); - return _nameToMember.GetValueOrDefault(name); - } - private SourceMembers PossibleNames() - { - var nameToMember = new SourceMembers(ReadAccessors.Length, StringComparer.OrdinalIgnoreCase); - IEnumerable accessors = ReadAccessors; - if (Config.MethodMappingEnabled) + if (_nameToMember == null) { - accessors = AddMethods(accessors); + SetNameToMember(); } - foreach (var member in accessors) + if (_nameToMember.TryGetValue(name, out var member) && Config.MethodMappingEnabled && member is GenericMethod genericMethod) { - if (!nameToMember.TryAdd(member.Name, member)) + return genericMethod.Close(); + } + return member; + void SetNameToMember() + { + _nameToMember = new(ReadAccessors.Length, StringComparer.OrdinalIgnoreCase); + IEnumerable accessors = ReadAccessors; + if (Config.MethodMappingEnabled) { - continue; + accessors = AddMethods(accessors); } - if (Config.Postfixes.Count == 0 && Config.Prefixes.Count == 0) + foreach (var member in accessors) { - continue; + _nameToMember.TryAdd(member.Name, member); + if (Config.Postfixes.Count == 0 && Config.Prefixes.Count == 0) + { + continue; + } + CheckPrePostfixes(member); } - CheckPrePostfixes(nameToMember, member); } - return nameToMember; - } - private IEnumerable AddMethods(IEnumerable accessors) - { - var publicNoArgMethods = GetPublicNoArgMethods(); - var publicNoArgExtensionMethods = GetPublicNoArgExtensionMethods(Config.SourceExtensionMethods.Where(Config.ShouldMapMethod)); - return accessors.Concat(publicNoArgMethods).Concat(publicNoArgExtensionMethods); + IEnumerable AddMethods(IEnumerable accessors) + { + var publicNoArgMethods = GetPublicNoArgMethods(); + var noArgExtensionMethods = GetNoArgExtensionMethods(Config.SourceExtensionMethods.Where(m => + !_nameToMember.ContainsKey(m.Name) && Config.ShouldMapMethod(m))); + return accessors.Concat(publicNoArgMethods).Concat(noArgExtensionMethods); + } + IEnumerable GetPublicNoArgMethods() => Type.GetMethods(BindingFlags.Instance | BindingFlags.Public).Where(m => + m.DeclaringType != typeof(object) && m.ReturnType != typeof(void) && !m.IsGenericMethodDefinition && !_nameToMember.ContainsKey(m.Name) && + Config.ShouldMapMethod(m) && m.GetParameters().Length == 0); + void CheckPrePostfixes(MemberInfo member) + { + foreach (var memberName in PossibleNames(member.Name, Config.Prefixes, Config.Postfixes)) + { + _nameToMember.TryAdd(memberName, member); + } + } + IEnumerable GetNoArgExtensionMethods(IEnumerable sourceExtensionMethodSearch) + { + var extensionMethods = (IEnumerable) + sourceExtensionMethodSearch.Where(method => !method.ContainsGenericParameters && method.FirstParameterType().IsAssignableFrom(Type)); + var genericInterfaces = Type.GetInterfaces().Where(t => t.IsGenericType); + if (Type.IsInterface && Type.IsGenericType) + { + genericInterfaces = genericInterfaces.Prepend(Type); + } + if (!genericInterfaces.Any()) + { + return extensionMethods; + } + var definitions = genericInterfaces.GroupBy(t => t.GetGenericTypeDefinition()).ToDictionary(g => g.Key, g => g.First()); + return extensionMethods.Concat( + from method in sourceExtensionMethodSearch + let targetType = method.FirstParameterType() + where targetType.IsInterface && targetType.ContainsGenericParameters + let genericInterface = definitions.GetValueOrDefault(targetType.GetGenericTypeDefinition()) + where genericInterface != null + select new GenericMethod(method, genericInterface)); + } } - private void CheckPrePostfixes(SourceMembers nameToMember, MemberInfo member) + class GenericMethod : MemberInfo { - foreach (var memberName in PossibleNames(member.Name, Config.Prefixes, Config.Postfixes)) + readonly MethodInfo _genericMethod; + readonly Type _genericInterface; + MethodInfo _closedMethod = ExpressionBuilder.DecTypeDepthInfo; + public GenericMethod(MethodInfo genericMethod, Type genericInterface) { - nameToMember.TryAdd(memberName, member); + _genericMethod = genericMethod; + _genericInterface = genericInterface; } + public MethodInfo Close() + { + if (_closedMethod == ExpressionBuilder.DecTypeDepthInfo) + { + // Use method.MakeGenericMethod(genericArguments) wrapped in a try/catch(ArgumentException) + // in order to catch exceptions resulting from the generic arguments not being compatible + // with any constraints that may be on the generic method's generic parameters. + try + { + _closedMethod = _genericMethod.MakeGenericMethod(_genericInterface.GenericTypeArguments); + } + catch (ArgumentException) + { + _closedMethod = null; + } + } + return _closedMethod; + } + public override Type DeclaringType => throw new NotImplementedException(); + public override MemberTypes MemberType => throw new NotImplementedException(); + public override string Name => _genericMethod.Name; + public override string ToString() => Name; + public override Type ReflectedType => throw new NotImplementedException(); + public override object[] GetCustomAttributes(bool inherit) => throw new NotImplementedException(); + public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotImplementedException(); + public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException(); } public static IEnumerable PossibleNames(string memberName, List prefixes, List postfixes) { @@ -75,7 +141,7 @@ public static IEnumerable PossibleNames(string memberName, List { continue; } - var withoutPrefix = memberName.Substring(prefix.Length); + var withoutPrefix = memberName[prefix.Length..]; yield return withoutPrefix; foreach (var s in PostFixes(postfixes, withoutPrefix)) { @@ -95,7 +161,7 @@ private static IEnumerable PostFixes(List postfixes, string name { continue; } - yield return name.Remove(name.Length - postfix.Length); + yield return name[..^postfix.Length]; } } public Type Type { get; } @@ -103,50 +169,6 @@ private static IEnumerable PostFixes(List postfixes, string name public MemberInfo[] ReadAccessors => _readAccessors ??= BuildReadAccessors(); public MemberInfo[] WriteAccessors => _writeAccessors ??= BuildWriteAccessors(); public ConstructorParameters[] Constructors => _constructors ??= GetConstructors(); - private IEnumerable GetPublicNoArgExtensionMethods(IEnumerable sourceExtensionMethodSearch) - { - var explicitExtensionMethods = sourceExtensionMethodSearch.Where(method => method.GetParameters()[0].ParameterType.IsAssignableFrom(Type)); - var genericInterfaces = Type.GetInterfaces().Where(t => t.IsGenericType); - if (Type.IsInterface && Type.IsGenericType) - { - genericInterfaces = genericInterfaces.Union(new[] { Type }); - } - return explicitExtensionMethods.Union - ( - from genericInterface in genericInterfaces - let genericInterfaceArguments = genericInterface.GenericTypeArguments - let matchedMethods = ( - from extensionMethod in sourceExtensionMethodSearch - where !extensionMethod.IsGenericMethodDefinition - select extensionMethod - ).Concat( - from extensionMethod in sourceExtensionMethodSearch - where extensionMethod.IsGenericMethodDefinition - && extensionMethod.GetGenericArguments().Length == genericInterfaceArguments.Length - let constructedGeneric = MakeGenericMethod(extensionMethod, genericInterfaceArguments) - where constructedGeneric != null - select constructedGeneric - ) - from methodMatch in matchedMethods - where methodMatch.GetParameters()[0].ParameterType.IsAssignableFrom(genericInterface) - select methodMatch - ); - - // Use method.MakeGenericMethod(genericArguments) wrapped in a try/catch(ArgumentException) - // in order to catch exceptions resulting from the generic arguments not being compatible - // with any constraints that may be on the generic method's generic parameters. - static MethodInfo MakeGenericMethod(MethodInfo genericMethod, Type[] genericArguments) - { - try - { - return genericMethod.MakeGenericMethod(genericArguments); - } - catch (ArgumentException) - { - return null; - } - } - } private MemberInfo[] BuildReadAccessors() { // Multiple types may define the same property (e.g. the class and multiple interfaces) - filter this to one of those properties @@ -175,14 +197,11 @@ private MemberInfo[] BuildWriteAccessors() private static bool FieldReadable(FieldInfo fieldInfo) => true; private static bool PropertyWritable(PropertyInfo propertyInfo) => propertyInfo.CanWrite || propertyInfo.PropertyType.IsCollection(); private static bool FieldWritable(FieldInfo fieldInfo) => !fieldInfo.IsInitOnly; - private IEnumerable GetTypeInheritance() => Type.IsInterface ? new[] { Type }.Concat(Type.GetInterfaces()) : Type.GetTypeInheritance(); + private IEnumerable GetTypeInheritance() => Type.IsInterface ? Type.GetInterfaces().Prepend(Type) : Type.GetTypeInheritance(); private IEnumerable GetProperties(Func propertyAvailableFor) => GetTypeInheritance().SelectMany(type => type.GetProperties(TypeExtensions.InstanceFlags).Where(property => propertyAvailableFor(property) && Config.ShouldMapProperty(property))); private IEnumerable GetFields(Func fieldAvailableFor) => GetTypeInheritance().SelectMany(type => type.GetFields(TypeExtensions.InstanceFlags).Where(field => fieldAvailableFor(field) && Config.ShouldMapField(field))); - private IEnumerable GetPublicNoArgMethods() => - Type.GetMethods(BindingFlags.Instance | BindingFlags.Public) - .Where(m => m.DeclaringType != typeof(object) && m.ReturnType != typeof(void) && Config.ShouldMapMethod(m) && m.GetParameters().Length == 0); } public readonly struct ConstructorParameters { diff --git a/src/AutoMapper/Mappers/CollectionMapper.cs b/src/AutoMapper/Mappers/CollectionMapper.cs index b6ff7e320c..4a8afb6eda 100644 --- a/src/AutoMapper/Mappers/CollectionMapper.cs +++ b/src/AutoMapper/Mappers/CollectionMapper.cs @@ -157,6 +157,9 @@ static class ArrayMapper private static readonly MethodInfo CopyToMethod = typeof(Array).GetMethod("CopyTo", new[] { typeof(Array), typeof(int) }); private static readonly MethodInfo CountMethod = typeof(Enumerable).StaticGenericMethod("Count", parametersCount: 1); private static readonly MethodInfo MapMultidimensionalMethod = typeof(ArrayMapper).GetStaticMethod(nameof(MapMultidimensional)); + private static readonly ParameterExpression Index = Variable(typeof(int), "destinationArrayIndex"); + private static readonly BinaryExpression ResetIndex = Assign(Index, Zero); + private static readonly UnaryExpression IncrementIndex = PostIncrementAssign(Index); private static Array MapMultidimensional(Array source, Type destinationElementType, ResolutionContext context) { var sourceElementType = source.GetType().GetElementType(); @@ -199,11 +202,10 @@ public static Expression MapToArray(IGlobalConfiguration configurationProvider, } var itemParam = Parameter(sourceElementType, "sourceItem"); var itemExpr = configurationProvider.MapExpression(profileMap, new TypePair(sourceElementType, destinationElementType), itemParam); - var indexParam = Parameter(typeof(int), "destinationArrayIndex"); - var setItem = Assign(ArrayAccess(destination, PostIncrementAssign(indexParam)), itemExpr); - return Block(new[] { destination, indexParam }, + var setItem = Assign(ArrayAccess(destination, IncrementIndex), itemExpr); + return Block(new[] { destination, Index }, createDestination, - Assign(indexParam, Zero), + ResetIndex, ForEach(itemParam, sourceExpression, setItem), destination); Expression MapFromArray() diff --git a/src/AutoMapper/Mappers/ConstructorMapper.cs b/src/AutoMapper/Mappers/ConstructorMapper.cs index ada7882747..c0b1c65c51 100644 --- a/src/AutoMapper/Mappers/ConstructorMapper.cs +++ b/src/AutoMapper/Mappers/ConstructorMapper.cs @@ -12,7 +12,7 @@ private static ConstructorInfo GetConstructor(Type sourceType, Type destinationT public Expression MapExpression(IGlobalConfiguration configurationProvider, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression) { var constructor = GetConstructor(sourceExpression.Type, destExpression.Type); - return Expression.New(constructor, ToType(sourceExpression, constructor.GetParameters()[0].ParameterType)); + return Expression.New(constructor, ToType(sourceExpression, constructor.FirstParameterType())); } } } \ No newline at end of file diff --git a/src/AutoMapper/Mappers/ConversionOperatorMapper.cs b/src/AutoMapper/Mappers/ConversionOperatorMapper.cs index 1729895a5d..6f790e18eb 100644 --- a/src/AutoMapper/Mappers/ConversionOperatorMapper.cs +++ b/src/AutoMapper/Mappers/ConversionOperatorMapper.cs @@ -23,7 +23,7 @@ private MethodInfo GetConversionOperator(Type sourceType, Type destinationType) public Expression MapExpression(IGlobalConfiguration configurationProvider, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression) { var conversionOperator = GetConversionOperator(sourceExpression.Type, destExpression.Type); - return Expression.Call(conversionOperator, ToType(sourceExpression, conversionOperator.GetParameters()[0].ParameterType)); + return Expression.Call(conversionOperator, ToType(sourceExpression, conversionOperator.FirstParameterType())); } } } diff --git a/src/AutoMapper/ProfileMap.cs b/src/AutoMapper/ProfileMap.cs index 54287e09e4..0c292fbc2e 100644 --- a/src/AutoMapper/ProfileMap.cs +++ b/src/AutoMapper/ProfileMap.cs @@ -154,7 +154,7 @@ public void Configure(IGlobalConfiguration configurationProvider) } private void BuildTypeMap(IGlobalConfiguration configurationProvider, ITypeMapConfiguration config) { - var typeMap = new TypeMap(config.SourceType, config.DestinationType, this, config.IsReverseMap); + var typeMap = new TypeMap(config.SourceType, config.DestinationType, this, config); config.Configure(typeMap); configurationProvider.RegisterTypeMap(typeMap); } @@ -203,7 +203,7 @@ public TypeMap CreateClosedGenericTypeMap(ITypeMapConfiguration openMapConfig, T TypeMap closedMap; lock (configurationProvider) { - closedMap = new TypeMap(closedTypes.SourceType, closedTypes.DestinationType, this); + closedMap = new TypeMap(closedTypes.SourceType, closedTypes.DestinationType, this, openMapConfig); } openMapConfig.Configure(closedMap); Configure(closedMap, configurationProvider); diff --git a/src/AutoMapper/PropertyMap.cs b/src/AutoMapper/PropertyMap.cs index 230c2d96ef..4f6149bdce 100644 --- a/src/AutoMapper/PropertyMap.cs +++ b/src/AutoMapper/PropertyMap.cs @@ -97,7 +97,7 @@ public void ApplyInheritedPropertyMap(PropertyMap inheritedMappedProperty) } } public override bool CanResolveValue => _canResolveValue ??= !Ignored && (_sourceMembers.Length > 0 || IsResolveConfigured); - private bool IsResolveConfigured => (ValueResolverConfig ?? CustomMapFunction ?? CustomMapExpression ?? (object)ValueConverterConfig) != null; + public bool IsResolveConfigured => (ValueResolverConfig ?? CustomMapFunction ?? CustomMapExpression ?? (object)ValueConverterConfig) != null; public void AddValueTransformation(ValueTransformerConfiguration valueTransformerConfiguration) { _valueTransformerConfigs ??= new(); diff --git a/src/AutoMapper/QueryableExtensions/ProjectionBuilder.cs b/src/AutoMapper/QueryableExtensions/ProjectionBuilder.cs index 92571c9117..fcb1dbe3eb 100644 --- a/src/AutoMapper/QueryableExtensions/ProjectionBuilder.cs +++ b/src/AutoMapper/QueryableExtensions/ProjectionBuilder.cs @@ -106,7 +106,9 @@ bool OverMaxDepth() } void ProjectProperties() { - foreach (var propertyMap in typeMap.PropertyMaps.Where(pm => pm.CanResolveValue && pm.DestinationMember.CanBeSet()).OrderBy(pm => pm.DestinationMember.MetadataToken)) + foreach (var propertyMap in typeMap.PropertyMaps.Where(pm => + pm.CanResolveValue && pm.DestinationMember.CanBeSet() && !typeMap.ConstructorParameterMatches(pm.DestinationName)) + .OrderBy(pm => pm.DestinationMember.MetadataToken)) { var propertyProjection = TryProjectMember(propertyMap, propertyMap.ExplicitExpansion); if (propertyProjection != null) @@ -231,7 +233,7 @@ public override QueryExpressions GetSubQueryExpression(ProjectionBuilder builder MapFromSource : path.GetSourceExpression(instanceParameter), Property : path.GetPropertyDescription(), path.Marker)).ToArray(); - var properties = letMapInfos.Select(m => m.Property).Concat(GetMemberAccessesVisitor.Retrieve(projection, instanceParameter)); + var properties = letMapInfos.Select(m => m.Property).Concat(GePropertiesVisitor.Retrieve(projection, instanceParameter)); var letType = ProxyGenerator.GetSimilarType(typeof(object), properties); TypeMap letTypeMap; lock(ConfigurationProvider) @@ -290,11 +292,11 @@ public Expression GetSourceExpression(Expression parameter) internal bool IsEquivalentTo(SubQueryPath other) => LetExpression == other.LetExpression && _members.Length == other._members.Length && _members.Take(_members.Length - 1).Zip(other._members, (left, right) => left.MemberMap == right.MemberMap).All(item => item); } - class GetMemberAccessesVisitor : ExpressionVisitor + class GePropertiesVisitor : ExpressionVisitor { private readonly Expression _target; - public List Members { get; } = new(); - public GetMemberAccessesVisitor(Expression target) => _target = target; + public HashSet Members { get; } = new(); + public GePropertiesVisitor(Expression target) => _target = target; protected override Expression VisitMember(MemberExpression node) { if(node.Expression == _target) @@ -305,7 +307,7 @@ protected override Expression VisitMember(MemberExpression node) } public static IEnumerable Retrieve(Expression expression, Expression target) { - var visitor = new GetMemberAccessesVisitor(target); + var visitor = new GePropertiesVisitor(target); visitor.Visit(expression); return visitor.Members.Select(member => new PropertyDescription(member.Name, member.GetMemberType())); } @@ -347,7 +349,7 @@ public int IncrementDepth(in ProjectionRequest request) BuiltProjections[request] = depth; return depth; } - public virtual Expression GetSubQueryMarker(LambdaExpression letExpression) => null; + public virtual Expression GetSubQueryMarker(LambdaExpression letExpression) => letExpression.Body; public virtual void Push(MemberProjection memberProjection) { } public virtual MemberPath GetCurrentPath() => MemberPath.Empty; public virtual void Pop() {} diff --git a/src/AutoMapper/QueryableExtensions/ProjectionMappers/EnumerableProjectionMapper.cs b/src/AutoMapper/QueryableExtensions/ProjectionMappers/EnumerableProjectionMapper.cs index ba48b40fde..848ee8ba5c 100644 --- a/src/AutoMapper/QueryableExtensions/ProjectionMappers/EnumerableProjectionMapper.cs +++ b/src/AutoMapper/QueryableExtensions/ProjectionMappers/EnumerableProjectionMapper.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Linq.Expressions; @@ -19,7 +20,8 @@ public bool IsMatch(MemberMap memberMap, TypeMap memberTypeMap, Expression resol memberMap.DestinationType.IsCollection() && memberMap.SourceType.IsCollection(); public Expression Project(IGlobalConfiguration configuration, MemberMap memberMap, TypeMap memberTypeMap, in ProjectionRequest request, Expression resolvedSource, LetPropertyMaps letPropertyMaps) { - var destinationListType = GetElementType(memberMap.DestinationType); + var destinationType = memberMap.DestinationType; + var destinationListType = GetElementType(destinationType); var sourceListType = GetElementType(memberMap.SourceType); var sourceExpression = resolvedSource; if (sourceListType != destinationListType) @@ -32,10 +34,22 @@ public Expression Project(IGlobalConfiguration configuration, MemberMap memberMa } sourceExpression = transformedExpressions.Chain(sourceExpression, Select); } - if (!memberMap.DestinationType.IsAssignableFrom(sourceExpression.Type)) + if (!destinationType.IsAssignableFrom(sourceExpression.Type)) { - var convertFunction = memberMap.DestinationType.IsArray ? ToArrayMethod : ToListMethod; - sourceExpression = Call(convertFunction.MakeGenericMethod(destinationListType), sourceExpression); + var convertFunction = destinationType.IsArray ? ToArrayMethod : ToListMethod; + convertFunction = convertFunction.MakeGenericMethod(destinationListType); + if (destinationType.IsAssignableFrom(convertFunction.ReturnType)) + { + sourceExpression = Call(convertFunction, sourceExpression); + } + else + { + var ctorInfo = destinationType.GetConstructor(new[] { sourceExpression.Type }); + if (ctorInfo is not null) + { + sourceExpression = New(ctorInfo, sourceExpression); + } + } } return sourceExpression; } diff --git a/src/AutoMapper/TypeMap.cs b/src/AutoMapper/TypeMap.cs index 90eefa572e..4eedfb1a8e 100644 --- a/src/AutoMapper/TypeMap.cs +++ b/src/AutoMapper/TypeMap.cs @@ -32,10 +32,15 @@ public class TypeMap private HashSet _inheritedTypeMaps; private HashSet _includedMembersTypeMaps; private List _valueTransformerConfigs; - public TypeMap(Type sourceType, Type destinationType, ProfileMap profile, bool isReverseMap = false) + private Type _destinationTypeOverride; + public TypeMap(Type sourceType, Type destinationType, ProfileMap profile, ITypeMapConfiguration typeMapConfiguration = null) { Types = new(sourceType, destinationType); Profile = profile; + if (typeMapConfiguration?.HasTypeConverter is true) + { + return; + } SourceTypeDetails = profile.CreateTypeDetails(sourceType); DestinationTypeDetails = profile.CreateTypeDetails(destinationType); var sourceMembers = new List(); @@ -43,7 +48,8 @@ public TypeMap(Type sourceType, Type destinationType, ProfileMap profile, bool i { sourceMembers.Clear(); var propertyType = destinationProperty.GetMemberType(); - if (profile.MapDestinationPropertyToSource(SourceTypeDetails, destinationType, propertyType, destinationProperty.Name, sourceMembers, isReverseMap)) + if (profile.MapDestinationPropertyToSource(SourceTypeDetails, destinationType, propertyType, destinationProperty.Name, sourceMembers, + typeMapConfiguration?.IsReverseMap is true)) { AddPropertyMap(destinationProperty, propertyType, sourceMembers); } @@ -70,7 +76,7 @@ public PathMap FindOrCreatePathMapFor(LambdaExpression destinationExpression, Me private void AddPathMap(PathMap pathMap) => _pathMaps.Add(pathMap.MemberPath, pathMap); public Features Features => _features ??= new(); public LambdaExpression MapExpression { get; private set; } - internal bool CanConstructorMap() => Profile.ConstructorMappingEnabled && !DestinationType.IsAbstract && !ConstructDestinationUsingServiceLocator && + internal bool CanConstructorMap() => Profile.ConstructorMappingEnabled && !DestinationType.IsAbstract && !ConstructDestinationUsingServiceLocator && !CustomConstruction && !HasTypeConverter && DestinationConstructors.Length > 0; public TypePair Types; public ConstructorMap ConstructorMap { get; set; } @@ -83,7 +89,15 @@ internal bool CanConstructorMap() => Profile.ConstructorMappingEnabled && !Desti public LambdaExpression CustomMapExpression { get; set; } public LambdaExpression CustomCtorFunction { get; set; } public LambdaExpression CustomCtorExpression { get; set; } - public Type DestinationTypeOverride { get; set; } + public Type DestinationTypeOverride + { + get => _destinationTypeOverride; + set + { + _destinationTypeOverride = value; + _sealed = true; + } + } public Type DestinationTypeToUse => DestinationTypeOverride ?? DestinationType; public bool ConstructDestinationUsingServiceLocator { get; set; } public bool IncludeAllDerivedTypes { get; set; } @@ -148,7 +162,7 @@ private IEnumerable GetUntypedIncludedMembers() => SourceType.IsGenericTypeDefinition ? Array.Empty() : IncludedMembersNames.Select(name => ExpressionBuilder.MemberAccessLambda(SourceType, name)); - public bool ConstructorParameterMatches(string destinationPropertyName) => ConstructorMap[destinationPropertyName] != null; + public bool ConstructorParameterMatches(string destinationPropertyName) => ConstructorMapping && ConstructorMap[destinationPropertyName] != null; public void AddPropertyMap(MemberInfo destProperty, Type destinationPropertyType, IEnumerable sourceMembers) { var propertyMap = new PropertyMap(destProperty, destinationPropertyType, this); @@ -168,12 +182,9 @@ public string[] GetUnmappedPropertyNames() { properties = Profile.CreateTypeDetails(DestinationType).WriteAccessors .Select(p => p.Name) + .Where(p => !ConstructorParameterMatches(p)) .Except(autoMappedProperties) .Except(PathMaps.Select(p => p.MemberPath.First.Name)); - if (ConstructorMapping) - { - properties = properties.Where(p => !ConstructorParameterMatches(p)); - } } else { @@ -210,7 +221,7 @@ public PropertyMap FindOrCreatePropertyMapFor(MemberInfo destinationProperty, Ty AddPropertyMap(propertyMap); return propertyMap; } - public TypePair GetAsPair() => new TypePair(SourceType, DestinationTypeOverride); + public TypePair AsPair() => new(SourceType, DestinationTypeOverride); public void IncludeDerivedTypes(TypePair derivedTypes) { CheckDifferent(derivedTypes); @@ -254,7 +265,7 @@ public void AddValueTransformation(ValueTransformerConfiguration valueTransforme _valueTransformerConfigs ??= new(); _valueTransformerConfigs.Add(valueTransformerConfiguration); } - public void Seal(IGlobalConfiguration configurationProvider, HashSet typeMapsPath = null) + public void Seal(IGlobalConfiguration configurationProvider, HashSet typeMapsPath) { if (_sealed) { diff --git a/src/IntegrationTests/AutoMapper.IntegrationTests.csproj b/src/IntegrationTests/AutoMapper.IntegrationTests.csproj index 89294efffa..2fc9fa80f5 100644 --- a/src/IntegrationTests/AutoMapper.IntegrationTests.csproj +++ b/src/IntegrationTests/AutoMapper.IntegrationTests.csproj @@ -2,7 +2,6 @@ net6.0 - true $(NoWarn);618 @@ -11,9 +10,9 @@ - + - + \ No newline at end of file diff --git a/src/IntegrationTests/BuiltInTypes/ByteArray.cs b/src/IntegrationTests/BuiltInTypes/ByteArray.cs index 1b54202cef..7d37b6ecd4 100644 --- a/src/IntegrationTests/BuiltInTypes/ByteArray.cs +++ b/src/IntegrationTests/BuiltInTypes/ByteArray.cs @@ -8,7 +8,7 @@ namespace AutoMapper.IntegrationTests.BuiltInTypes; -public class ByteArrayColumns : AutoMapperSpecBase, IAsyncLifetime +public class ByteArrayColumns : IntegrationTest { public class Customer { @@ -32,7 +32,7 @@ public class Context : LocalDbContext public DbSet Customers { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -64,13 +64,4 @@ public void Can_map_with_projection() }); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/BuiltInTypes/ProjectUsing.cs b/src/IntegrationTests/BuiltInTypes/ConvertUsing.cs similarity index 65% rename from src/IntegrationTests/BuiltInTypes/ProjectUsing.cs rename to src/IntegrationTests/BuiltInTypes/ConvertUsing.cs index 4c19e6a3c8..ce5bc172c6 100644 --- a/src/IntegrationTests/BuiltInTypes/ProjectUsing.cs +++ b/src/IntegrationTests/BuiltInTypes/ConvertUsing.cs @@ -1,15 +1,13 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; -using System.Threading.Tasks; -using AutoMapper.UnitTests; using Microsoft.EntityFrameworkCore; using Shouldly; using Xunit; namespace AutoMapper.IntegrationTests.BuiltInTypes; -public class ProjectUsingWithNullables : AutoMapperSpecBase, IAsyncLifetime +public class ConvertUsingWithNullables : IntegrationTest { public class MyProfile : Profile { @@ -41,7 +39,7 @@ public class MyTableModel public MyEnum EnumValueNullable { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(TestContext context) { @@ -73,18 +71,9 @@ public void Should_project_ok() results[1].EnumValueNullable.ShouldBe(MyEnum.Value1); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class ProjectUsingBug : AutoMapperSpecBase, IAsyncLifetime +public class ConvertUsingBug : IntegrationTest { public class Parent { @@ -136,13 +125,45 @@ public void can_map_with_projection() var result = ProjectTo(db.Parents); } } - - public async Task InitializeAsync() + public class DatabaseInitializer : DropCreateDatabaseAlways { } +} +public class StringTypeConverter : IntegrationTest +{ + protected override MapperConfiguration CreateConfiguration() => new(cfg => { - var initializer = new CreateDatabaseIfNotExists(); - - await initializer.Migrate(); + cfg.CreateMap().ConvertUsing(s => s ?? string.Empty); + cfg.CreateMap().ForMember(a => a.Subject, o => o.MapFrom(p => p.Libelle ?? p.Service.Libelle)); + }); + [Fact] + public void Should_work() + { + using var context = new ApplicationDBContext(); + var query = ProjectTo(context.Planning); + query.Single().Subject.ShouldBe("Test"); + } + public class Appointment + { + public int Id { get; set; } + public string Subject { get; set; } + } + public class Planning + { + public int Id { get; set; } + public string Libelle { get; set; } + public Service Service { get; set; } + } + public partial class Service + { + public int Id { get; set; } + public string Libelle { get; set; } + } + public partial class ApplicationDBContext : LocalDbContext + { + public DbSet Planning { get; set; } + public DbSet Service { get; set; } + } + public class DatabaseInitializer : DropCreateDatabaseAlways + { + protected override void Seed(ApplicationDBContext context) => context.Planning.Add(new() { Libelle = "Test" }); } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/BuiltInTypes/DateTimeToNullableDateTime.cs b/src/IntegrationTests/BuiltInTypes/DateTimeToNullableDateTime.cs index 8eef1e3df3..e78c2831ec 100644 --- a/src/IntegrationTests/BuiltInTypes/DateTimeToNullableDateTime.cs +++ b/src/IntegrationTests/BuiltInTypes/DateTimeToNullableDateTime.cs @@ -8,7 +8,7 @@ namespace AutoMapper.IntegrationTests.BuiltInTypes; -public class DateTimeToNullableDateTime : AutoMapperSpecBase, IAsyncLifetime +public class DateTimeToNullableDateTime : IntegrationTest { public class Parent { @@ -45,13 +45,4 @@ public void Should_not_fail() ProjectTo(context.Parents).Single().Date.ShouldBe(_expected); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/BuiltInTypes/Enums.cs b/src/IntegrationTests/BuiltInTypes/Enums.cs index b1341aebf3..6a14a77e6b 100644 --- a/src/IntegrationTests/BuiltInTypes/Enums.cs +++ b/src/IntegrationTests/BuiltInTypes/Enums.cs @@ -8,7 +8,7 @@ namespace AutoMapper.IntegrationTests.BuiltInTypes; -public class EnumToUnderlyingType : AutoMapperSpecBase, IAsyncLifetime +public class EnumToUnderlyingType : IntegrationTest { public class Customer { @@ -27,7 +27,7 @@ public class Context : LocalDbContext { public DbSet Customers { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -44,17 +44,8 @@ public void Can_map_with_projection() ProjectTo(context.Customers).First().ConsoleColor.ShouldBe((int)ConsoleColor.Yellow); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class UnderlyingTypeToEnum : AutoMapperSpecBase, IAsyncLifetime +public class UnderlyingTypeToEnum : IntegrationTest { public class Customer { @@ -73,7 +64,7 @@ public class Context : LocalDbContext { public DbSet Customers { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -90,17 +81,8 @@ public void Can_map_with_projection() ProjectTo(context.Customers).First().ConsoleColor.ShouldBe(ConsoleColor.Yellow); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class EnumToEnum : AutoMapperSpecBase, IAsyncLifetime +public class EnumToEnum : IntegrationTest { public class Customer { @@ -119,7 +101,7 @@ public class Context : LocalDbContext { public DbSet Customers { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -136,13 +118,4 @@ public void Can_map_with_projection() ProjectTo(context.Customers).First().ConsoleColor.ShouldBe(ConsoleColor.DarkYellow); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/BuiltInTypes/NullableToNonNullable.cs b/src/IntegrationTests/BuiltInTypes/NullableToNonNullable.cs index 0a87132917..ee0d0fdd85 100644 --- a/src/IntegrationTests/BuiltInTypes/NullableToNonNullable.cs +++ b/src/IntegrationTests/BuiltInTypes/NullableToNonNullable.cs @@ -8,7 +8,7 @@ namespace AutoMapper.IntegrationTests.BuiltInTypes; -public class NullableLongToLong : AutoMapperSpecBase, IAsyncLifetime +public class NullableLongToLong : IntegrationTest { public class Customer { @@ -30,7 +30,7 @@ public class Context : LocalDbContext public DbSet Customers { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -60,18 +60,9 @@ public void Can_map_with_projection() model.LastName.ShouldBe("Smith"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class NullableIntToLong : AutoMapperSpecBase, IAsyncLifetime +public class NullableIntToLong : IntegrationTest { public class Customer { @@ -93,7 +84,7 @@ public class Context : LocalDbContext public DbSet Customers { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -123,13 +114,4 @@ public void Can_map_with_projection() model.LastName.ShouldBe("Smith"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/BuiltInTypes/ProjectEnumerableOfIntToHashSet.cs b/src/IntegrationTests/BuiltInTypes/ProjectEnumerableOfIntToHashSet.cs new file mode 100644 index 0000000000..4fc1f4142b --- /dev/null +++ b/src/IntegrationTests/BuiltInTypes/ProjectEnumerableOfIntToHashSet.cs @@ -0,0 +1,68 @@ +using Microsoft.EntityFrameworkCore; +using Shouldly; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Xunit; + +namespace AutoMapper.IntegrationTests.BuiltInTypes; + +public class ProjectEnumerableOfIntToHashSet : IntegrationTest +{ + public class Customer + { + [Key] + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public List Items { get; set; } + } + + public class Item + { + public int Id { get; set; } + } + + public class CustomerViewModel + { + public string FirstName { get; set; } + public string LastName { get; set; } + public HashSet ItemsIds { get; set; } + } + + public class Context : LocalDbContext + { + public DbSet Customers { get; set; } + public DbSet Items { get; set; } + } + + public class DatabaseInitializer : DropCreateDatabaseAlways + { + protected override void Seed(Context context) + { + context.Customers.Add(new Customer + { + FirstName = "Bob", + LastName = "Smith", + Items = new List(new[] { new Item(), new Item(), new Item() }) + }); + + base.Seed(context); + } + } + + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateProjection().ForMember(d => d.ItemsIds, o => o.MapFrom(s => s.Items.Select(i => i.Id))); + }); + + [Fact] + public void Can_map_with_projection() + { + using (var context = new Context()) + { + var customer = ProjectTo(context.Customers).Single(); + customer.ItemsIds.SequenceEqual(new int[] { 1, 2, 3 }).ShouldBeTrue(); + } + } +} \ No newline at end of file diff --git a/src/IntegrationTests/BuiltInTypes/ProjectEnumerableOfIntToList.cs b/src/IntegrationTests/BuiltInTypes/ProjectEnumerableOfIntToList.cs index 0bbe59fef9..2c5e0804bf 100644 --- a/src/IntegrationTests/BuiltInTypes/ProjectEnumerableOfIntToList.cs +++ b/src/IntegrationTests/BuiltInTypes/ProjectEnumerableOfIntToList.cs @@ -9,7 +9,7 @@ namespace AutoMapper.IntegrationTests.BuiltInTypes; -public class ProjectEnumerableOfIntToList : AutoMapperSpecBase, IAsyncLifetime +public class ProjectEnumerableOfIntToList : IntegrationTest { public class Customer { @@ -67,13 +67,4 @@ public void Can_map_with_projection() customer.ItemsIds.SequenceEqual(new int[] { 1, 2, 3 }).ShouldBeTrue(); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ChildClassTests.cs b/src/IntegrationTests/ChildClassTests.cs index 3aa6119111..c55e0e1cfe 100644 --- a/src/IntegrationTests/ChildClassTests.cs +++ b/src/IntegrationTests/ChildClassTests.cs @@ -60,7 +60,7 @@ protected override void Seed(TestContext testContext) } - public class UnitTest : AutoMapperSpecBase, IAsyncLifetime + public class UnitTest : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -103,16 +103,6 @@ public void AutoMapperEFRelationsTest() } [Fact] public void MapShouldThrow() => new Action(() => Mapper.Map(new Sub())).ShouldThrow().Message.ShouldBe("CreateProjection works with ProjectTo, not with Map."); - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } - } } \ No newline at end of file diff --git a/src/IntegrationTests/CreateDatabaseIfNotExists.cs b/src/IntegrationTests/CreateDatabaseIfNotExists.cs deleted file mode 100644 index d6ef719cd2..0000000000 --- a/src/IntegrationTests/CreateDatabaseIfNotExists.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; - -namespace AutoMapper.IntegrationTests; - -public class CreateDatabaseIfNotExists : DropCreateDatabaseAlways - where TContext : DbContext, new() -{ - -} - -public class DropCreateDatabaseAlways where TContext : DbContext, new() -{ - protected virtual void Seed(TContext context) - { - - } - - public async Task Migrate() - { - await using var context = new TContext(); - - await context.Database.EnsureDeletedAsync(); - await context.Database.EnsureCreatedAsync(); - - Seed(context); - - await context.SaveChangesAsync(); - } -} \ No newline at end of file diff --git a/src/IntegrationTests/CustomMapFrom/CustomMapFromTest.cs b/src/IntegrationTests/CustomMapFrom/CustomMapFromTest.cs index dd70b2ca6e..cf9dea3547 100644 --- a/src/IntegrationTests/CustomMapFrom/CustomMapFromTest.cs +++ b/src/IntegrationTests/CustomMapFrom/CustomMapFromTest.cs @@ -1,113 +1,92 @@ using System.ComponentModel.DataAnnotations; using System.Linq; -using System.Threading.Tasks; -using AutoMapper.UnitTests; using Microsoft.EntityFrameworkCore; using Shouldly; using Xunit; -namespace AutoMapper.IntegrationTests.CustomMapFrom +namespace AutoMapper.IntegrationTests.CustomMapFrom; + +public class CustomMapFromTest : IntegrationTest { - namespace CustomMapFromTest + protected override MapperConfiguration CreateConfiguration() => new(cfg => cfg.CreateProjection() + .ForMember(x => x.FullAddress, o => o.MapFrom(c => c.Address.Street + ", " + c.Address.City + " " + c.Address.State))); + [Fact] + public void can_map_with_projection() { - public class Customer + using (var context = new Context()) { - [Key] - public int Id { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } + var customerVms = context.Customers.Select(c => new CustomerViewModel + { + FirstName = c.FirstName, + LastName = c.LastName, + FullAddress = c.Address.Street + ", " + c.Address.City + " " + c.Address.State + }).ToList(); - public Address Address { get; set; } - } + customerVms.ForEach(x => + { + x.FullAddress.ShouldNotBeNull(); + x.FullAddress.ShouldNotBeEmpty(); + }); - public class Address - { - [Key] - public int Id { get; set; } - public string Street { get; set; } - public string City { get; set; } - public string State { get; set; } + customerVms = ProjectTo(context.Customers).ToList(); + customerVms.ForEach(x => + { + x.FullAddress.ShouldNotBeNull(); + x.FullAddress.ShouldNotBeEmpty(); + }); } + } + public class Customer + { + [Key] + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } - public class CustomerViewModel - { - public string FirstName { get; set; } - public string LastName { get; set; } + public Address Address { get; set; } + } - public string FullAddress { get; set; } - } + public class Address + { + [Key] + public int Id { get; set; } + public string Street { get; set; } + public string City { get; set; } + public string State { get; set; } + } - public class Context : LocalDbContext - { - public DbSet Customers { get; set; } - public DbSet
Addresses { get; set; } + public class CustomerViewModel + { + public string FirstName { get; set; } + public string LastName { get; set; } - } + public string FullAddress { get; set; } + } - public class DatabaseInitializer : CreateDatabaseIfNotExists - { - protected override void Seed(Context context) - { - context.Customers.Add(new Customer - { - FirstName = "Bob", - LastName = "Smith", - Address = new Address - { - Street = "123 Anywhere", - City = "Austin", - State = "TX" - } - }); + public class Context : LocalDbContext + { + public DbSet Customers { get; set; } + public DbSet
Addresses { get; set; } - base.Seed(context); - } - } - - public class AutoMapperQueryableExtensionsThrowsNullReferenceExceptionSpec : AutoMapperSpecBase, IAsyncLifetime - { - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateProjection() - .ForMember(x => x.FullAddress, - o => o.MapFrom(c => c.Address.Street + ", " + c.Address.City + " " + c.Address.State)); - }); + } - [Fact] - public void can_map_with_projection() + public class DatabaseInitializer : DropCreateDatabaseAlways + { + protected override void Seed(Context context) + { + context.Customers.Add(new Customer { - using (var context = new Context()) + FirstName = "Bob", + LastName = "Smith", + Address = new Address { - var customerVms = context.Customers.Select(c => new CustomerViewModel - { - FirstName = c.FirstName, - LastName = c.LastName, - FullAddress = c.Address.Street + ", " + c.Address.City + " " + c.Address.State - }).ToList(); - - customerVms.ForEach(x => - { - x.FullAddress.ShouldNotBeNull(); - x.FullAddress.ShouldNotBeEmpty(); - }); - - customerVms = ProjectTo(context.Customers).ToList(); - customerVms.ForEach(x => - { - x.FullAddress.ShouldNotBeNull(); - x.FullAddress.ShouldNotBeEmpty(); - }); + Street = "123 Anywhere", + City = "Austin", + State = "TX" } - } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } + }); - public Task DisposeAsync() => Task.CompletedTask; + base.Seed(context); } } } \ No newline at end of file diff --git a/src/IntegrationTests/CustomMapFrom/MapObjectPropertyFromSubQuery.cs b/src/IntegrationTests/CustomMapFrom/MapObjectPropertyFromSubQuery.cs index 2b4d1010bd..737a39ce14 100644 --- a/src/IntegrationTests/CustomMapFrom/MapObjectPropertyFromSubQuery.cs +++ b/src/IntegrationTests/CustomMapFrom/MapObjectPropertyFromSubQuery.cs @@ -11,7 +11,7 @@ namespace AutoMapper.IntegrationTests.CustomMapFrom; -public class MemberWithSubQueryProjections : AutoMapperSpecBase, IAsyncLifetime +public class MemberWithSubQueryProjections : IntegrationTest { public class Customer { @@ -79,17 +79,8 @@ public void Should_work() result.FirstItem.Code.ShouldBe(1); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class MemberWithSubQueryProjectionsNoMap : AutoMapperSpecBase, IAsyncLifetime +public class MemberWithSubQueryProjectionsNoMap : IntegrationTest { public class Customer { @@ -150,17 +141,8 @@ public void Should_work() result.FirstItem.Code.ShouldBe(1); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class MapObjectPropertyFromSubQueryTypeNameMax : AutoMapperSpecBase, IAsyncLifetime +public class MapObjectPropertyFromSubQueryTypeNameMax : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -256,7 +238,7 @@ public class ProductModel public int VeryLongColumnNameVeryLongColumnNameVeryLongColumnNameVeryLongColumnNameVeryLongColumnName11 { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -264,22 +246,13 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Products { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class MapObjectPropertyFromSubQueryExplicitExpansion : AutoMapperSpecBase, IAsyncLifetime +public class MapObjectPropertyFromSubQueryExplicitExpansion : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -353,7 +326,7 @@ public class ProductModel public PriceModel Price { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -361,23 +334,14 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Products { get; set; } public DbSet
Articles { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class MapObjectPropertyFromSubQuery : AutoMapperSpecBase, IAsyncLifetime +public class MapObjectPropertyFromSubQuery : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -451,7 +415,7 @@ public class ProductModel public PriceModel Price { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -459,22 +423,13 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Products { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class MapObjectPropertyFromSubQueryWithInnerObject : AutoMapperSpecBase, IAsyncLifetime +public class MapObjectPropertyFromSubQueryWithInnerObject : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -552,7 +507,7 @@ public class ProductModel public PriceModel Price { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -562,23 +517,14 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Products { get; set; } public DbSet ProductArticles { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class MapObjectPropertyFromSubQueryWithCollection : AutoMapperSpecBase, IAsyncLifetime +public class MapObjectPropertyFromSubQueryWithCollection : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -662,7 +608,7 @@ public class ProductModel public PriceModel Price { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -671,20 +617,11 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Products { get; set; } public DbSet ProductArticles { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } public class MapObjectPropertyFromSubQueryWithCollectionSameName : NonValidatingSpecBase, IAsyncLifetime @@ -788,7 +725,7 @@ public class ArticlesModel public ICollection Articles { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -797,7 +734,7 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Products { get; set; } public DbSet ProductArticles { get; set; } @@ -812,7 +749,7 @@ public async Task InitializeAsync() public Task DisposeAsync() => Task.CompletedTask; } -public class SubQueryWithMapFromNullable : AutoMapperSpecBase, IAsyncLifetime +public class SubQueryWithMapFromNullable : IntegrationTest { // Source Types public class Cable @@ -863,7 +800,7 @@ public class CableEndModel public int? DataHallId { get; set; } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Cables { get; set; } public DbSet CableEnds { get; set; } @@ -875,7 +812,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -913,17 +850,9 @@ public void Should_project_ok() result.AnotherEnd.DataHallId.ShouldBeNull(); } } - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class MapObjectPropertyFromSubQueryCustomSource : AutoMapperSpecBase, IAsyncLifetime +public class MapObjectPropertyFromSubQueryCustomSource : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -971,7 +900,7 @@ public class OwnerDto public string Name { get; set; } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Owners { get; set; } public DbSet Products { get; set; } @@ -979,7 +908,7 @@ class ClientContext : LocalDbContext public DbSet ProductReviews { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -1003,17 +932,9 @@ public void Should_project_ok() results.Any(result => result?.Brand == null).ShouldBeTrue(); } } - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class MemberWithSubQueryIdentity : AutoMapperSpecBase, IAsyncLifetime +public class MemberWithSubQueryIdentity : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new MapperConfiguration(cfg => { @@ -1064,7 +985,7 @@ public class CEntity public string SubString { get; set; } public BEntity BEntity { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -1086,7 +1007,7 @@ protected override void Seed(ClientContext context) }); } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -1106,13 +1027,4 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } public DbSet AEntities { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/CustomProjection.cs b/src/IntegrationTests/CustomProjection.cs index 094b7694b4..d1e4170911 100644 --- a/src/IntegrationTests/CustomProjection.cs +++ b/src/IntegrationTests/CustomProjection.cs @@ -7,7 +7,7 @@ namespace AutoMapper.IntegrationTests; -public class CustomProjectionStringToString : AutoMapperSpecBase, IAsyncLifetime +public class CustomProjectionStringToString : IntegrationTest { public class TestContext : LocalDbContext { @@ -58,19 +58,8 @@ class TargetChild { public string Greeting { get; set; } } - - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } -public class CustomProjectionCustomClasses : AutoMapperSpecBase, IAsyncLifetime +public class CustomProjectionCustomClasses : IntegrationTest { public class TestContext : LocalDbContext { @@ -120,19 +109,8 @@ class TargetChild { public string Greeting { get; set; } } - - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } -public class CustomProjectionChildClasses : AutoMapperSpecBase, IAsyncLifetime +public class CustomProjectionChildClasses : IntegrationTest { public class TestContext : LocalDbContext { @@ -182,15 +160,4 @@ class TargetChild { public string Greeting { get; set; } } - - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/ExpandCollections.cs b/src/IntegrationTests/ExplicitExpansion/ExpandCollections.cs index 08df6fd9ad..7ebf44e3ea 100644 --- a/src/IntegrationTests/ExplicitExpansion/ExpandCollections.cs +++ b/src/IntegrationTests/ExplicitExpansion/ExpandCollections.cs @@ -10,7 +10,7 @@ namespace AutoMapper.IntegrationTests.ExplicitExpansion; -public class ExpandCollections : AutoMapperSpecBase, IAsyncLifetime +public class ExpandCollections : IntegrationTest { TrainingCourseDto _course; @@ -24,10 +24,14 @@ public class ExpandCollections : AutoMapperSpecBase, IAsyncLifetime [Fact] public void Should_expand_collections_items() { + using (var context = new ClientContext()) + { + _course = ProjectTo(context.TrainingCourses, null, c => c.Content.Select(co => co.Category)).FirstOrDefault(n => n.CourseName == "Course 1"); + } _course.Content[0].Category.CategoryName.ShouldBe("Category 1"); } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -40,7 +44,7 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Categories { get; set; } public DbSet TrainingCourses { get; set; } @@ -97,17 +101,4 @@ public class TrainingContentDto public CategoryDto Category { get; set; } } - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - - using (var context = new ClientContext()) - { - _course = ProjectTo(context.TrainingCourses, null, c => c.Content.Select(co => co.Category)).FirstOrDefault(n => n.CourseName == "Course 1"); - } - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/ExpandCollectionsWithStrings.cs b/src/IntegrationTests/ExplicitExpansion/ExpandCollectionsWithStrings.cs index 86f802a9e3..c071c35fec 100644 --- a/src/IntegrationTests/ExplicitExpansion/ExpandCollectionsWithStrings.cs +++ b/src/IntegrationTests/ExplicitExpansion/ExpandCollectionsWithStrings.cs @@ -10,7 +10,7 @@ namespace AutoMapper.IntegrationTests.ExplicitExpansion; -public class ExpandCollectionsWithStrings : AutoMapperSpecBase, IAsyncLifetime +public class ExpandCollectionsWithStrings : IntegrationTest { TrainingCourseDto _course; @@ -24,10 +24,14 @@ public class ExpandCollectionsWithStrings : AutoMapperSpecBase, IAsyncLifetime [Fact] public void Should_expand_collections_items_with_strings() { + using (var context = new ClientContext()) + { + _course = ProjectTo(context.TrainingCourses, null, "Content.Category").FirstOrDefault(n => n.CourseName == "Course 1"); + } _course.Content[0].Category.CategoryName.ShouldBe("Category 1"); } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -40,7 +44,7 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Categories { get; set; } public DbSet TrainingCourses { get; set; } @@ -97,17 +101,4 @@ public class TrainingContentDto public CategoryDto Category { get; set; } } - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - - using (var context = new ClientContext()) - { - _course = ProjectTo(context.TrainingCourses, null, "Content.Category").FirstOrDefault(n => n.CourseName == "Course 1"); - } - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/ExpandMembersPath.cs b/src/IntegrationTests/ExplicitExpansion/ExpandMembersPath.cs index 8f015f4f04..9f36925040 100644 --- a/src/IntegrationTests/ExplicitExpansion/ExpandMembersPath.cs +++ b/src/IntegrationTests/ExplicitExpansion/ExpandMembersPath.cs @@ -10,7 +10,7 @@ namespace AutoMapper.IntegrationTests.ExplicitExpansion; -public class ExpandMembersPath : AutoMapperSpecBase, IAsyncLifetime +public class ExpandMembersPath : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -82,7 +82,7 @@ public class TestContext : LocalDbContext public DbSet Class3Set { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(TestContext context) { @@ -146,12 +146,4 @@ public class Class3 public Class2 Class2 { get; set; } } - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/ExplicitlyExpandCollectionsAndChildReferences.cs b/src/IntegrationTests/ExplicitExpansion/ExplicitlyExpandCollectionsAndChildReferences.cs index 3d00d8d3b2..28220a127a 100644 --- a/src/IntegrationTests/ExplicitExpansion/ExplicitlyExpandCollectionsAndChildReferences.cs +++ b/src/IntegrationTests/ExplicitExpansion/ExplicitlyExpandCollectionsAndChildReferences.cs @@ -10,7 +10,7 @@ namespace AutoMapper.IntegrationTests.ExplicitExpansion; -public class ExplicitlyExpandCollectionsAndChildReferences : AutoMapperSpecBase, IAsyncLifetime +public class ExplicitlyExpandCollectionsAndChildReferences : IntegrationTest { TrainingCourseDto _course; @@ -24,10 +24,14 @@ public class ExplicitlyExpandCollectionsAndChildReferences : AutoMapperSpecBase, [Fact] public void Should_expand_collections_items() { + using (var context = new ClientContext()) + { + _course = ProjectTo(context.TrainingCourses, null, c => c.Content.Select(co => co.Category)).FirstOrDefault(n => n.CourseName == "Course 1"); + } _course.Content[0].Category.CategoryName.ShouldBe("Category 1"); } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -40,7 +44,7 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Categories { get; set; } public DbSet TrainingCourses { get; set; } @@ -97,17 +101,4 @@ public class TrainingContentDto public CategoryDto Category { get; set; } } - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - - using (var context = new ClientContext()) - { - _course = ProjectTo(context.TrainingCourses, null, c => c.Content.Select(co => co.Category)).FirstOrDefault(n => n.CourseName == "Course 1"); - } - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/NestedExplicitExpand.cs b/src/IntegrationTests/ExplicitExpansion/NestedExplicitExpand.cs index 61dbf654d2..7654776718 100644 --- a/src/IntegrationTests/ExplicitExpansion/NestedExplicitExpand.cs +++ b/src/IntegrationTests/ExplicitExpansion/NestedExplicitExpand.cs @@ -8,7 +8,7 @@ namespace AutoMapper.IntegrationTests.ExplicitExpansion; -public class NestedExplicitExpand : AutoMapperSpecBase, IAsyncLifetime +public class NestedExplicitExpand : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -80,7 +80,7 @@ public class TestContext : LocalDbContext public DbSet Class3Set { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(TestContext context) { @@ -144,12 +144,4 @@ public class Class3 public Class2 Class2 { get; set; } } - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/NestedExplicitExpandWithFields.cs b/src/IntegrationTests/ExplicitExpansion/NestedExplicitExpandWithFields.cs index 75229f95ad..f54d529ac9 100644 --- a/src/IntegrationTests/ExplicitExpansion/NestedExplicitExpandWithFields.cs +++ b/src/IntegrationTests/ExplicitExpansion/NestedExplicitExpandWithFields.cs @@ -8,7 +8,7 @@ namespace AutoMapper.IntegrationTests.ExplicitExpansion; -public class NestedExplicitExpandWithFields : AutoMapperSpecBase, IAsyncLifetime +public class NestedExplicitExpandWithFields : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => { @@ -80,7 +80,7 @@ public class TestContext : LocalDbContext public DbSet Class3Set { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(TestContext context) { @@ -144,13 +144,4 @@ public class Class3 public Class2 Class2 { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/ProjectAndAllowNullCollections.cs b/src/IntegrationTests/ExplicitExpansion/ProjectAndAllowNullCollections.cs index 41ad64150b..a29e878d1f 100644 --- a/src/IntegrationTests/ExplicitExpansion/ProjectAndAllowNullCollections.cs +++ b/src/IntegrationTests/ExplicitExpansion/ProjectAndAllowNullCollections.cs @@ -9,7 +9,7 @@ namespace AutoMapper.IntegrationTests.ExplicitExpansion; -public class ProjectAndAllowNullCollections : AutoMapperSpecBase, IAsyncLifetime +public class ProjectAndAllowNullCollections : IntegrationTest { public class Foo { @@ -137,13 +137,4 @@ public void Should_work() foos[0].Bazs.ShouldBeNull(); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ExplicitExpansion/ProjectionWithExplicitExpansion.cs b/src/IntegrationTests/ExplicitExpansion/ProjectionWithExplicitExpansion.cs index b1f426a417..b71a5bfbd4 100644 --- a/src/IntegrationTests/ExplicitExpansion/ProjectionWithExplicitExpansion.cs +++ b/src/IntegrationTests/ExplicitExpansion/ProjectionWithExplicitExpansion.cs @@ -27,7 +27,7 @@ public static void SqlFromShouldStartWith (this string sqlSelect, string tableN // Example of Value Type mapped to appropriate Nullable -public class ProjectionWithExplicitExpansion : AutoMapperSpecBase, IAsyncLifetime +public class ProjectionWithExplicitExpansion : IntegrationTest { public class SourceDeepInner { @@ -82,7 +82,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) private static readonly Source _iqf = _iq.First(); - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -224,13 +224,4 @@ public void ProjectDeepInner() sqlSelect.SqlShouldNotSelectColumn(nameof(_iqf.Desc)); dto.Desc.ShouldBeNull(); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ICollectionAggregateProjections.cs b/src/IntegrationTests/ICollectionAggregateProjections.cs index 6601332c63..e61d42e711 100644 --- a/src/IntegrationTests/ICollectionAggregateProjections.cs +++ b/src/IntegrationTests/ICollectionAggregateProjections.cs @@ -11,7 +11,7 @@ namespace AutoMapper.IntegrationTests; using QueryableExtensions; using System.Collections.Generic; -public class ICollectionAggregateProjections : AutoMapperSpecBase, IAsyncLifetime +public class ICollectionAggregateProjections : IntegrationTest { public class Customer { @@ -79,15 +79,4 @@ public void Can_map_with_projection() result.ItemCodesSum.ShouldBe(9); } } - - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } \ No newline at end of file diff --git a/src/IntegrationTests/IEnumerableAggregateProjections.cs b/src/IntegrationTests/IEnumerableAggregateProjections.cs index fcaa46180e..ad3ac23559 100644 --- a/src/IntegrationTests/IEnumerableAggregateProjections.cs +++ b/src/IntegrationTests/IEnumerableAggregateProjections.cs @@ -11,7 +11,7 @@ namespace AutoMapper.IntegrationTests; using QueryableExtensions; using System.Collections.Generic; -public class IEnumerableAggregateProjections : AutoMapperSpecBase, IAsyncLifetime +public class IEnumerableAggregateProjections : IntegrationTest { public class Customer { @@ -79,15 +79,4 @@ public void Can_map_with_projection() result.ItemCodesSum.ShouldBe(9); } } - - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } \ No newline at end of file diff --git a/src/IntegrationTests/IEnumerableMemberProjections.cs b/src/IntegrationTests/IEnumerableMemberProjections.cs index 3ecd5c0ea9..07f0b2673a 100644 --- a/src/IntegrationTests/IEnumerableMemberProjections.cs +++ b/src/IntegrationTests/IEnumerableMemberProjections.cs @@ -11,7 +11,7 @@ namespace AutoMapper.IntegrationTests; using QueryableExtensions; using System.Collections.Generic; -public class IEnumerableMemberProjections : AutoMapperSpecBase, IAsyncLifetime +public class IEnumerableMemberProjections : IntegrationTest { public class Customer { @@ -80,14 +80,4 @@ public void Can_map_to_ienumerable() result.Items.Count().ShouldBe(3); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } \ No newline at end of file diff --git a/src/IntegrationTests/IncludeMembers.cs b/src/IntegrationTests/IncludeMembers.cs index 60ee7274d7..f55eec94cd 100644 --- a/src/IntegrationTests/IncludeMembers.cs +++ b/src/IntegrationTests/IncludeMembers.cs @@ -10,41 +10,41 @@ namespace AutoMapper.IntegrationTests; -public class IncludeMembers : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembers : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public InnerSource InnerSource { get; set; } public OtherInnerSource OtherInnerSource { get; set; } } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Title { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Title { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -72,52 +72,42 @@ public void Should_flatten() result.Title.ShouldBe("title"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } -public class IncludeMembersExplicitExpansion : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembersExplicitExpansion : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public InnerSource InnerSource { get; set; } public OtherInnerSource OtherInnerSource { get; set; } } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Title { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Title { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -145,35 +135,25 @@ public void Should_flatten() result.Title.ShouldBe("title"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } -public class IncludeMembersFirstOrDefault : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembersFirstOrDefault : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public List InnerSources { get; set; } = new List(); public List OtherInnerSources { get; set; } = new List(); } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Publisher { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } @@ -181,7 +161,7 @@ class OtherInnerSource public string Title { get; set; } public string Author { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } @@ -190,11 +170,11 @@ class Destination public string Author { get; set; } public string Publisher { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -229,35 +209,25 @@ public void Should_flatten() result.Publisher.ShouldBe("publisher"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; - } -public class IncludeMembersFirstOrDefaultWithMapFromExpression : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembersFirstOrDefaultWithMapFromExpression : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public List InnerSources { get; set; } = new List(); public List OtherInnerSources { get; set; } = new List(); } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public string Description1 { get; set; } public string Publisher { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } @@ -265,7 +235,7 @@ class OtherInnerSource public string Title1 { get; set; } public string Author { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } @@ -274,11 +244,11 @@ class Destination public string Author { get; set; } public string Publisher { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -313,72 +283,63 @@ public void Should_flatten() result.Publisher.ShouldBe("publisher"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class IncludeMembersFirstOrDefaultWithSubqueryMapFrom : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembersFirstOrDefaultWithSubqueryMapFrom : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public List InnerSources { get; set; } = new List(); public List OtherInnerSources { get; set; } = new List(); } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public List InnerSourceDetails { get; } = new List(); } - class InnerSourceDetails + public class InnerSourceDetails { public int Id { get; set; } public string Description { get; set; } public string Publisher { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public List OtherInnerSourceDetails { get; } = new List(); } - class OtherInnerSourceDetails + public class OtherInnerSourceDetails { public int Id { get; set; } public string Title { get; set; } public string Author { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public DestinationDetails Details { get; set; } public OtherDestinationDetails OtherDetails { get; set; } } - class DestinationDetails + public class DestinationDetails { public string Description { get; set; } public string Publisher { get; set; } } - class OtherDestinationDetails + public class OtherDestinationDetails { public string Title { get; set; } public string Author { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -415,82 +376,73 @@ public void Should_flatten() result.OtherDetails.Author.ShouldBe("author"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class IncludeMembersSelectFirstOrDefaultWithSubqueryMapFrom : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembersSelectFirstOrDefaultWithSubqueryMapFrom : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public List InnerSourceWrappers { get; set; } = new List(); public List OtherInnerSources { get; set; } = new List(); } - class InnerSourceWrapper + public class InnerSourceWrapper { public int Id { get; set; } public InnerSource InnerSource { get; set; } } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public List InnerSourceDetailsWrapper { get; } = new List(); } - class InnerSourceDetailsWrapper + public class InnerSourceDetailsWrapper { public int Id { get; set; } public InnerSourceDetails InnerSourceDetails { get; set; } } - class InnerSourceDetails + public class InnerSourceDetails { public int Id { get; set; } public string Description { get; set; } public string Publisher { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public List OtherInnerSourceDetails { get; } = new List(); } - class OtherInnerSourceDetails + public class OtherInnerSourceDetails { public int Id { get; set; } public string Title { get; set; } public string Author { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public DestinationDetails Details { get; set; } public OtherDestinationDetails OtherDetails { get; set; } } - class DestinationDetails + public class DestinationDetails { public string Description { get; set; } public string Publisher { get; set; } } - class OtherDestinationDetails + public class OtherDestinationDetails { public string Title { get; set; } public string Author { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -528,72 +480,63 @@ public void Should_flatten() result.OtherDetails.Author.ShouldBe("author"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class SubqueryMapFromWithIncludeMembersFirstOrDefault : AutoMapperSpecBase, IAsyncLifetime +public class SubqueryMapFromWithIncludeMembersFirstOrDefault : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public List InnerSources { get; set; } = new List(); public List OtherInnerSources { get; set; } = new List(); } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public List InnerSourceDetails { get; } = new List(); } - class InnerSourceDetails + public class InnerSourceDetails { public int Id { get; set; } public string Description { get; set; } public string Publisher { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public List OtherInnerSourceDetails { get; } = new List(); } - class OtherInnerSourceDetails + public class OtherInnerSourceDetails { public int Id { get; set; } public string Title { get; set; } public string Author { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public DestinationDetails Details { get; set; } public OtherDestinationDetails OtherDetails { get; set; } } - class DestinationDetails + public class DestinationDetails { public string Description { get; set; } public string Publisher { get; set; } } - class OtherDestinationDetails + public class OtherDestinationDetails { public string Title { get; set; } public string Author { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -632,72 +575,63 @@ public void Should_flatten() result.OtherDetails.Author.ShouldBe("author"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class SubqueryMapFromWithIncludeMembersSelectFirstOrDefault : AutoMapperSpecBase, IAsyncLifetime +public class SubqueryMapFromWithIncludeMembersSelectFirstOrDefault : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public List InnerSources { get; set; } = new List(); public List OtherInnerSources { get; set; } = new List(); } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public List InnerSourceDetails { get; } = new List(); } - class InnerSourceDetails + public class InnerSourceDetails { public int Id { get; set; } public string Description { get; set; } public string Publisher { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public List OtherInnerSourceDetails { get; } = new List(); } - class OtherInnerSourceDetails + public class OtherInnerSourceDetails { public int Id { get; set; } public string Title { get; set; } public string Author { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public DestinationDetails Details { get; set; } public OtherDestinationDetails OtherDetails { get; set; } } - class DestinationDetails + public class DestinationDetails { public string Description { get; set; } public string Publisher { get; set; } } - class OtherDestinationDetails + public class OtherDestinationDetails { public string Title { get; set; } public string Author { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -736,82 +670,73 @@ public void Should_flatten() result.OtherDetails.Author.ShouldBe("author"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class SubqueryMapFromWithIncludeMembersSelectMemberFirstOrDefault : AutoMapperSpecBase, IAsyncLifetime +public class SubqueryMapFromWithIncludeMembersSelectMemberFirstOrDefault : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public List InnerSourceWrappers { get; set; } = new List(); public List OtherInnerSources { get; set; } = new List(); } - class InnerSourceWrapper + public class InnerSourceWrapper { public int Id { get; set; } public InnerSource InnerSource { get; set; } } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public List InnerSourceDetailsWrapper { get; } = new List(); } - class InnerSourceDetailsWrapper + public class InnerSourceDetailsWrapper { public int Id { get; set; } public InnerSourceDetails InnerSourceDetails { get; set; } } - class InnerSourceDetails + public class InnerSourceDetails { public int Id { get; set; } public string Description { get; set; } public string Publisher { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public List OtherInnerSourceDetails { get; } = new List(); } - class OtherInnerSourceDetails + public class OtherInnerSourceDetails { public int Id { get; set; } public string Title { get; set; } public string Author { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public DestinationDetails Details { get; set; } public OtherDestinationDetails OtherDetails { get; set; } } - class DestinationDetails + public class DestinationDetails { public string Description { get; set; } public string Publisher { get; set; } } - class OtherDestinationDetails + public class OtherDestinationDetails { public string Title { get; set; } public string Author { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -851,51 +776,42 @@ public void Should_flatten() result.OtherDetails.Author.ShouldBe("author"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class IncludeMembersWithMapFromExpression : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembersWithMapFromExpression : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public InnerSource InnerSource { get; set; } public OtherInnerSource OtherInnerSource { get; set; } } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public string Description1 { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Title1 { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string Title { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -921,52 +837,43 @@ public void Should_flatten_with_MapFrom() result.Title.ShouldBe("title"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class IncludeMembersWithNullSubstitute : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembersWithNullSubstitute : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public InnerSource InnerSource { get; set; } public OtherInnerSource OtherInnerSource { get; set; } } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public int? Code { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public int? Code { get; set; } public int? OtherCode { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public int Code { get; set; } public int OtherCode { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -993,50 +900,41 @@ public void Should_flatten() result.OtherCode.ShouldBe(7); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class IncludeMembersMembersFirstOrDefaultWithNullSubstitute : AutoMapperSpecBase, IAsyncLifetime +public class IncludeMembersMembersFirstOrDefaultWithNullSubstitute : IntegrationTest { - class Source + public class Source { public int Id { get; set; } public string Name { get; set; } public List InnerSources { get; set; } = new List(); public List OtherInnerSources { get; set; } = new List(); } - class InnerSource + public class InnerSource { public int Id { get; set; } public string Name { get; set; } public int? Code { get; set; } } - class OtherInnerSource + public class OtherInnerSource { public int Id { get; set; } public string Name { get; set; } public int? Code { get; set; } public int? OtherCode { get; set; } } - class Destination + public class Destination { public int Id { get; set; } public string Name { get; set; } public int Code { get; set; } public int OtherCode { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -1064,17 +962,8 @@ public void Should_flatten() result.OtherCode.ShouldBe(7); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class CascadedIncludeMembers : AutoMapperSpecBase, IAsyncLifetime +public class CascadedIncludeMembers : IntegrationTest { public class Source { @@ -1102,11 +991,11 @@ public class Destination cfg.CreateProjection(MemberList.None).IncludeMembers(s => s.FieldLevel2); cfg.CreateProjection(MemberList.None); }); - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Sources { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -1126,13 +1015,4 @@ public void Should_flatten() result.TheField.ShouldBe(2); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/Inheritance/DerivedComplexTypes.cs b/src/IntegrationTests/Inheritance/DerivedComplexTypes.cs index 0a9c6a3f6a..c9c63bc08a 100644 --- a/src/IntegrationTests/Inheritance/DerivedComplexTypes.cs +++ b/src/IntegrationTests/Inheritance/DerivedComplexTypes.cs @@ -9,7 +9,7 @@ namespace AutoMapper.IntegrationTests.Inheritance; -public class DerivedComplexTypes : AutoMapperSpecBase, IAsyncLifetime +public class DerivedComplexTypes : IntegrationTest { [ComplexType] public class LocalizedString @@ -82,13 +82,4 @@ public void Can_map_with_projection() customerVm.Address.ShouldBe("home"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/Inheritance/OverrideDestinationMappingsTest.cs b/src/IntegrationTests/Inheritance/OverrideDestinationMappingsTest.cs index 12afc48e77..93bfcfe223 100644 --- a/src/IntegrationTests/Inheritance/OverrideDestinationMappingsTest.cs +++ b/src/IntegrationTests/Inheritance/OverrideDestinationMappingsTest.cs @@ -7,14 +7,14 @@ namespace AutoMapper.IntegrationTests.Inheritance; -public class OverrideDestinationMappingsTest : AutoMapperSpecBase, IAsyncLifetime +public class OverrideDestinationMappingsTest : IntegrationTest { public class Context : LocalDbContext { public DbSet Entity { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -83,13 +83,4 @@ public abstract class ChildModelBase public class ChildModel : ChildModelBase { } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/Inheritance/ProjectToAbstractType.cs b/src/IntegrationTests/Inheritance/ProjectToAbstractType.cs index c5fc98bd9a..23c4d80024 100644 --- a/src/IntegrationTests/Inheritance/ProjectToAbstractType.cs +++ b/src/IntegrationTests/Inheritance/ProjectToAbstractType.cs @@ -10,7 +10,7 @@ namespace AutoMapper.IntegrationTests.Inheritance; -public class ProjectToAbstractType : AutoMapperSpecBase, IAsyncLifetime +public class ProjectToAbstractType : IntegrationTest { ITypeA[] _destinations; @@ -32,7 +32,7 @@ public class DbEntityA public string Name { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -66,18 +66,9 @@ public void Should_project_to_abstract_type() _destinations.Length.ShouldBe(3); _destinations[2].Name.ShouldBe("Bill Gates"); } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class ProjectToInterface : AutoMapperSpecBase, IAsyncLifetime +public class ProjectToInterface : IntegrationTest { //Data Objects public class DataLayer @@ -253,7 +244,7 @@ public IEnumerable GetCalendarDays(ICalendar calendar) } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -351,13 +342,4 @@ public MyProfile() CreateProjection(); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/Inheritance/QueryableInterfaceInheritanceIssue.cs b/src/IntegrationTests/Inheritance/QueryableInterfaceInheritanceIssue.cs index 8af8b05b60..7e6e131674 100644 --- a/src/IntegrationTests/Inheritance/QueryableInterfaceInheritanceIssue.cs +++ b/src/IntegrationTests/Inheritance/QueryableInterfaceInheritanceIssue.cs @@ -7,7 +7,7 @@ namespace AutoMapper.IntegrationTests.Inheritance; -public class QueryableInterfaceInheritanceIssue : AutoMapperSpecBase, IAsyncLifetime +public class QueryableInterfaceInheritanceIssue : IntegrationTest { QueryableDto[] _result; @@ -30,7 +30,7 @@ public class QueryableDto public string Id { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -38,7 +38,7 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Entities { get; set; } } @@ -46,23 +46,13 @@ class ClientContext : LocalDbContext [Fact] public void QueryableShouldMapSpecifiedBaseInterfaceMember() { - _result.FirstOrDefault(dto => dto.Id == "One").ShouldNotBeNull(); - _result.FirstOrDefault(dto => dto.Id == "Two").ShouldNotBeNull(); - } - - protected override MapperConfiguration CreateConfiguration() => new(cfg => cfg.CreateProjection()); - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - using (var context = new ClientContext()) { _result = ProjectTo(context.Entities).ToArray(); } + _result.FirstOrDefault(dto => dto.Id == "One").ShouldNotBeNull(); + _result.FirstOrDefault(dto => dto.Id == "Two").ShouldNotBeNull(); } - public Task DisposeAsync() => Task.CompletedTask; + protected override MapperConfiguration CreateConfiguration() => new(cfg => cfg.CreateProjection()); } \ No newline at end of file diff --git a/src/IntegrationTests/IntegrationTest.cs b/src/IntegrationTests/IntegrationTest.cs new file mode 100644 index 0000000000..f9507df4ec --- /dev/null +++ b/src/IntegrationTests/IntegrationTest.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using AutoMapper.UnitTests; +using Microsoft.EntityFrameworkCore; +using Xunit; + +namespace AutoMapper.IntegrationTests; + +public abstract class IntegrationTest : AutoMapperSpecBase, IAsyncLifetime where TInitializer : IInitializer, new() +{ + Task IAsyncLifetime.DisposeAsync() => Task.CompletedTask; + Task IAsyncLifetime.InitializeAsync() => new TInitializer().Migrate(); +} +public interface IInitializer +{ + Task Migrate(); +} +public class DropCreateDatabaseAlways : IInitializer where TContext : DbContext, new() +{ + protected virtual void Seed(TContext context){} + public async Task Migrate() + { + await using var context = new TContext(); + + await context.Database.EnsureDeletedAsync(); + await context.Database.EnsureCreatedAsync(); + + Seed(context); + + await context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/src/IntegrationTests/LocalDbContext.cs b/src/IntegrationTests/LocalDbContext.cs index 6dbadfac17..7508762669 100644 --- a/src/IntegrationTests/LocalDbContext.cs +++ b/src/IntegrationTests/LocalDbContext.cs @@ -1,23 +1,10 @@ -using System.Globalization; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; namespace AutoMapper.IntegrationTests; public abstract class LocalDbContext : DbContext { - private readonly string _localDbVersion = "mssqllocaldb"; - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - var baseConnectionString = @"Integrated Security=True; MultipleActiveResultSets=True;"; - - var connectionString = string.Format( - CultureInfo.InvariantCulture, - @"Data Source=(localdb)\{1};{0};Database={2}", - baseConnectionString, - _localDbVersion, - GetType().FullName); - - optionsBuilder.UseSqlServer(connectionString); - } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer( + @$"Data Source=(localdb)\mssqllocaldb;Integrated Security=True;MultipleActiveResultSets=True;Database={GetType()};Connection Timeout=300", + o=>o.EnableRetryOnFailure()); } \ No newline at end of file diff --git a/src/IntegrationTests/MaxDepth/MaxDepthWithCollections.cs b/src/IntegrationTests/MaxDepth/MaxDepthWithCollections.cs index 3b5fa8517b..feff83633e 100644 --- a/src/IntegrationTests/MaxDepth/MaxDepthWithCollections.cs +++ b/src/IntegrationTests/MaxDepth/MaxDepthWithCollections.cs @@ -9,7 +9,7 @@ namespace AutoMapper.IntegrationTests.MaxDepth; -public class MaxDepthWithCollections : AutoMapperSpecBase, IAsyncLifetime +public class MaxDepthWithCollections : IntegrationTest { TrainingCourseDto _course; @@ -23,13 +23,17 @@ public class MaxDepthWithCollections : AutoMapperSpecBase, IAsyncLifetime [Fact] public void Should_project_with_MaxDepth() { + using (var context = new ClientContext()) + { + _course = ProjectTo(context.TrainingCourses).FirstOrDefault(n => n.CourseName == "Course 1"); + } _course.CourseName.ShouldBe("Course 1"); var content = _course.Content[0]; content.ContentName.ShouldBe("Content 1"); content.Course.ShouldBeNull(); } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -41,7 +45,7 @@ protected override void Seed(ClientContext context) } } - class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet TrainingCourses { get; set; } public DbSet TrainingContents { get; set; } @@ -84,18 +88,4 @@ public class TrainingContentDto public TrainingCourseDto Course { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - - using (var context = new ClientContext()) - { - _course = ProjectTo(context.TrainingCourses).FirstOrDefault(n => n.CourseName == "Course 1"); - } - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/MaxDepth/NavigationPropertySO.cs b/src/IntegrationTests/MaxDepth/NavigationPropertySO.cs index 6e862213bd..1f31761161 100644 --- a/src/IntegrationTests/MaxDepth/NavigationPropertySO.cs +++ b/src/IntegrationTests/MaxDepth/NavigationPropertySO.cs @@ -10,7 +10,7 @@ namespace AutoMapper.IntegrationTests.MaxDepth; -public class NavigationPropertySO : AutoMapperSpecBase, IAsyncLifetime +public class NavigationPropertySO : IntegrationTest { CustomerDTO _destination; @@ -62,7 +62,7 @@ public class Context : LocalDbContext public DbSet Custs { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -97,13 +97,4 @@ public void Can_map_with_projection() _destination.Cust.CustomerID.ShouldBe(1); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/MaxDepth/NestedDtos.cs b/src/IntegrationTests/MaxDepth/NestedDtos.cs index a02c171b7b..cf64683f3f 100644 --- a/src/IntegrationTests/MaxDepth/NestedDtos.cs +++ b/src/IntegrationTests/MaxDepth/NestedDtos.cs @@ -10,7 +10,7 @@ namespace AutoMapper.IntegrationTests.MaxDepth; -public class NestedDtos : AutoMapperSpecBase, IAsyncLifetime +public class NestedDtos : IntegrationTest { ArtDto _destination; @@ -53,7 +53,7 @@ public class TestContext : LocalDbContext public DbSet Arts { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(TestContext context) { @@ -76,21 +76,11 @@ protected override void Seed(TestContext context) [Fact] public void Should_project_nested_dto() { - _destination.AName.ShouldBe("art1"); - _destination.Sem.Name.ShouldBe("sem1"); - } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - using (var context = new TestContext()) { _destination = ProjectTo(context.Arts).FirstOrDefault(); } + _destination.AName.ShouldBe("art1"); + _destination.Sem.Name.ShouldBe("sem1"); } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/NullCheckCollections.cs b/src/IntegrationTests/NullCheckCollections.cs index ab7f3d0c07..aabbbb7d56 100644 --- a/src/IntegrationTests/NullCheckCollections.cs +++ b/src/IntegrationTests/NullCheckCollections.cs @@ -11,7 +11,7 @@ namespace AutoMapper.IntegrationTests; using System; using UnitTests; -public class NullCheckCollectionsFirstOrDefault : AutoMapperSpecBase, IAsyncLifetime +public class NullCheckCollectionsFirstOrDefault : IntegrationTest { public class SourceType { @@ -28,11 +28,11 @@ public class DestinationType { public int? Index { get; set; } } - class DatabaseInitializer : DropCreateDatabaseAlways + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(TestContext context) => context.SourceTypes.Add(new SourceType { Parameters = { new Parameter { Name = "Index", Value = 101 } } }); } - class TestContext : LocalDbContext + public class TestContext : LocalDbContext { public DbSet SourceTypes { get; set; } } @@ -46,17 +46,8 @@ public void Should_project_ok() ProjectTo(context.SourceTypes).Single().Index.ShouldBe(101); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class NullChildItemTest : AutoMapperSpecBase, IAsyncLifetime +public class NullChildItemTest : IntegrationTest { protected override MapperConfiguration CreateConfiguration() => new(cfg => cfg.CreateProjection()); public class TestContext : LocalDbContext @@ -109,17 +100,8 @@ public class GrandChild public int Id { get; set; } public int Value { get; set; } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class NullCheckCollections : AutoMapperSpecBase, IAsyncLifetime +public class NullCheckCollections : IntegrationTest { public class Student { @@ -178,13 +160,4 @@ public void Can_map_with_projection() ProjectTo(context.Students).Single().Name.ShouldBe("Bob"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/NullSubstitute.cs b/src/IntegrationTests/NullSubstitute.cs index 4054756b3f..92a08a080f 100644 --- a/src/IntegrationTests/NullSubstitute.cs +++ b/src/IntegrationTests/NullSubstitute.cs @@ -9,7 +9,7 @@ namespace AutoMapper.IntegrationTests; using UnitTests; -public class NullSubstitute : AutoMapperSpecBase, IAsyncLifetime +public class NullSubstitute : IntegrationTest { public class Customer { @@ -33,7 +33,7 @@ public class Context : LocalDbContext public DbSet Customers { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -60,17 +60,8 @@ public void Can_map_with_projection() ProjectTo(context.Customers).First().Value.ShouldBe(5); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class NullSubstituteWithStrings : AutoMapperSpecBase, IAsyncLifetime +public class NullSubstituteWithStrings : IntegrationTest { public class Customer { @@ -89,7 +80,7 @@ public class Context : LocalDbContext { public DbSet Customers { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -107,44 +98,35 @@ public void Can_map_with_projection() ProjectTo(context.Customers).First().Value.ShouldBe("5"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } -public class NullSubstituteWithEntity : AutoMapperSpecBase, IAsyncLifetime +public class NullSubstituteWithEntity : IntegrationTest { - class Customer + public class Customer { public int Id { get; set; } public Value Value { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } - class Value + public class Value { public int Id { get; set; } } - class CustomerViewModel + public class CustomerViewModel { public ValueViewModel Value { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } - class ValueViewModel + public class ValueViewModel { public int Id { get; set; } } - class Context : LocalDbContext + public class Context : LocalDbContext { public DbSet Customers { get; set; } } - class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -165,13 +147,4 @@ public void Can_map_with_projection() ProjectTo(context.Customers).First().Value.ShouldBeNull(); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ParameterizedQueries.cs b/src/IntegrationTests/ParameterizedQueries.cs index 97438ecc46..caf10efbcc 100644 --- a/src/IntegrationTests/ParameterizedQueries.cs +++ b/src/IntegrationTests/ParameterizedQueries.cs @@ -9,7 +9,7 @@ namespace AutoMapper.IntegrationTests; -public class ParameterizedQueries : AutoMapperSpecBase, IAsyncLifetime +public class ParameterizedQueries : IntegrationTest { public class Entity { @@ -24,12 +24,12 @@ public class EntityDto public string UserName { get; set; } } - private class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Entities { get; set; } } - private class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(ClientContext context) { @@ -98,13 +98,4 @@ protected override Expression VisitConstant(ConstantExpression node) return base.VisitConstant(node); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } \ No newline at end of file diff --git a/src/IntegrationTests/ProjectionAdvanced.cs b/src/IntegrationTests/ProjectionAdvanced.cs new file mode 100644 index 0000000000..5c3d0c060b --- /dev/null +++ b/src/IntegrationTests/ProjectionAdvanced.cs @@ -0,0 +1,39 @@ +using AutoMapper.UnitTests; +using Microsoft.EntityFrameworkCore; +using Shouldly; +using System.Linq; +using Xunit; +namespace AutoMapper.IntegrationTests; +public class ProjectionAdvanced : IntegrationTest +{ + protected override MapperConfiguration CreateConfiguration() => new(c => c.CreateProjection().Advanced().ForAllMembers(o=>o.Ignore())); + [Fact] + public void Should_work() + { + using var context = new Context(); + var dto = ProjectTo(context.Entities).Single(); + dto.Id.ShouldBe(0); + dto.Name.ShouldBeNull(); + dto.Value.ShouldBeNull(); + } + public class Initializer : DropCreateDatabaseAlways + { + protected override void Seed(Context context) => context.Add(new Entity { Name = "name", Value = "value" }); + } + public class Context : LocalDbContext + { + public DbSet Entities { get; set; } + } + public class Entity + { + public int Id { get; set; } + public string Name { get; set; } + public string Value { get; set; } + } + public class Dto + { + public int Id { get; set; } + public string Name { get; set; } + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/IntegrationTests/ProjectionOrderTest.cs b/src/IntegrationTests/ProjectionOrderTest.cs index 329703c38d..8b32e50d0e 100644 --- a/src/IntegrationTests/ProjectionOrderTest.cs +++ b/src/IntegrationTests/ProjectionOrderTest.cs @@ -8,7 +8,7 @@ namespace AutoMapper.IntegrationTests; -public class ProjectionOrderTest : AutoMapperSpecBase, IAsyncLifetime +public class ProjectionOrderTest : IntegrationTest { public class Destination { @@ -43,7 +43,7 @@ public class ChildSource : BaseEntity public string String { get; set; } } - private class ClientContext : LocalDbContext + public class ClientContext : LocalDbContext { public DbSet Source1 { get; set; } @@ -69,19 +69,7 @@ public void Should_Not_Throw_NotSupportedException_On_Union() ProjectTo(context.Source1).Union(ProjectTo(context.Source2)).ToString(); } } - - class DatabaseInitializer : DropCreateDatabaseAlways - { - - } - - public async Task InitializeAsync() + public class DatabaseInitializer : DropCreateDatabaseAlways { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); } - - public Task DisposeAsync() => Task.CompletedTask; - } \ No newline at end of file diff --git a/src/IntegrationTests/ValueTransformerTests.cs b/src/IntegrationTests/ValueTransformerTests.cs index 573a011a7d..d026c5e88c 100644 --- a/src/IntegrationTests/ValueTransformerTests.cs +++ b/src/IntegrationTests/ValueTransformerTests.cs @@ -9,7 +9,7 @@ namespace AutoMapper.IntegrationTests { namespace ValueTransformerTests { - public class BasicTransforming : AutoMapperSpecBase, IAsyncLifetime + public class BasicTransforming : IntegrationTest { public class Source { @@ -28,7 +28,7 @@ public class Context : LocalDbContext public DbSet Sources { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -54,18 +54,9 @@ public async Task Should_transform_value() dest.Value.ShouldBe("Jimmy is straight up dope"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } - public class StackingTransformers : AutoMapperSpecBase, IAsyncLifetime + public class StackingTransformers : IntegrationTest { public class Source { @@ -84,7 +75,7 @@ public class Context : LocalDbContext public DbSet Sources { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -111,18 +102,9 @@ public async Task Should_stack_transformers_in_order() dest.Value.ShouldBe("Jimmy is straight up dope! No joke!"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } - public class DifferentProfiles : AutoMapperSpecBase, IAsyncLifetime + public class DifferentProfiles : IntegrationTest { public class Source { @@ -148,7 +130,7 @@ public class Context : LocalDbContext public DbSet Sources { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -168,18 +150,9 @@ public async Task Should_not_apply_other_transform() dest.Value.ShouldBe("Jimmy is straight up dope"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } - public class StackingRootConfigAndProfileTransform : AutoMapperSpecBase, IAsyncLifetime + public class StackingRootConfigAndProfileTransform : IntegrationTest { public class Source { @@ -198,7 +171,7 @@ public class Context : LocalDbContext public DbSet Sources { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -227,18 +200,9 @@ public async Task ShouldApplyProfileFirstThenRoot() dest.Value.ShouldBe("Jimmy is straight up dope! No joke!"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } - public class TransformingValueTypes : AutoMapperSpecBase, IAsyncLifetime + public class TransformingValueTypes : IntegrationTest { public class Source { @@ -257,7 +221,7 @@ public class Context : LocalDbContext public DbSet Sources { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -286,18 +250,9 @@ public async Task ShouldApplyProfileFirstThenRoot() dest.Value.ShouldBe((5 + 3) * 2); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } - public class StackingRootAndProfileAndMemberConfig : AutoMapperSpecBase, IAsyncLifetime + public class StackingRootAndProfileAndMemberConfig : IntegrationTest { public class Source { @@ -316,7 +271,7 @@ public class Context : LocalDbContext public DbSet Sources { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -347,18 +302,9 @@ public async Task ShouldApplyTypeMapThenProfileThenRoot() dest.Value.ShouldBe("Jimmy, for real, is straight up dope! No joke!"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } - public class StackingTypeMapAndRootAndProfileAndMemberConfig : AutoMapperSpecBase, IAsyncLifetime + public class StackingTypeMapAndRootAndProfileAndMemberConfig : IntegrationTest { public class Source { @@ -377,7 +323,7 @@ public class Context : LocalDbContext public DbSet Sources { get; set; } } - public class DatabaseInitializer : CreateDatabaseIfNotExists + public class DatabaseInitializer : DropCreateDatabaseAlways { protected override void Seed(Context context) { @@ -408,17 +354,6 @@ public async Task ShouldApplyTypeMapThenProfileThenRoot() dest.Value.ShouldBe("Jimmy, seriously, for real, is straight up dope! No joke!"); } } - - public async Task InitializeAsync() - { - var initializer = new DatabaseInitializer(); - - await initializer.Migrate(); - } - - public Task DisposeAsync() => Task.CompletedTask; } - - } } \ No newline at end of file diff --git a/src/UnitTests/AutoMapper.UnitTests.csproj b/src/UnitTests/AutoMapper.UnitTests.csproj index 6124d9c446..9df7afc7b6 100644 --- a/src/UnitTests/AutoMapper.UnitTests.csproj +++ b/src/UnitTests/AutoMapper.UnitTests.csproj @@ -10,7 +10,7 @@ - + \ No newline at end of file diff --git a/src/UnitTests/AutoMapperSpecBase.cs b/src/UnitTests/AutoMapperSpecBase.cs index 1e6eab90bb..b465d2e7c8 100644 --- a/src/UnitTests/AutoMapperSpecBase.cs +++ b/src/UnitTests/AutoMapperSpecBase.cs @@ -18,6 +18,8 @@ public class IgnoreMapAttribute : Attribute } static class Utils { + public static IMappingExpression Advanced(this IProjectionExpression projection) => + (IMappingExpression)projection; public static TypeMap FindTypeMapFor(this IConfigurationProvider configurationProvider) => configurationProvider.Internal().FindTypeMapFor(); public static IReadOnlyCollection GetAllTypeMaps(this IConfigurationProvider configurationProvider) => configurationProvider.Internal().GetAllTypeMaps(); public static TypeMap ResolveTypeMap(this IConfigurationProvider configurationProvider, Type sourceType, Type destinationType) => configurationProvider.Internal().ResolveTypeMap(sourceType, destinationType); diff --git a/src/UnitTests/Bug/MapExpandoObjectProperty.cs b/src/UnitTests/Bug/MapExpandoObjectProperty.cs index 59c6319750..93c0db9533 100644 --- a/src/UnitTests/Bug/MapExpandoObjectProperty.cs +++ b/src/UnitTests/Bug/MapExpandoObjectProperty.cs @@ -1,4 +1,5 @@ using System.Dynamic; +using Xunit; namespace AutoMapper.UnitTests.Bug { @@ -19,11 +20,10 @@ class To { cfg.CreateMap(); }); - - protected override void Because_of() + [Fact] + public void Should_work() { dynamic baseSettings = new ExpandoObject(); - var settings = Mapper.Map(new From { ExpandoObject = baseSettings}); } } diff --git a/src/UnitTests/Bug/PreserveReferencesSameDestination.cs b/src/UnitTests/Bug/PreserveReferencesSameDestination.cs index ca45e87b62..add1e723bd 100644 --- a/src/UnitTests/Bug/PreserveReferencesSameDestination.cs +++ b/src/UnitTests/Bug/PreserveReferencesSameDestination.cs @@ -27,8 +27,8 @@ public BaseTypeDto() } protected override MapperConfiguration CreateConfiguration() => new(cfg=> cfg.CreateMap().PreserveReferences()); - - protected override void Because_of() + [Fact] + public void Should_work() { var baseType = new BaseType(); var baseTypeDto = new BaseTypeDto(); diff --git a/src/UnitTests/Bug/UseDestinationValue.cs b/src/UnitTests/Bug/UseDestinationValue.cs index 935783dbe4..5434d34b16 100644 --- a/src/UnitTests/Bug/UseDestinationValue.cs +++ b/src/UnitTests/Bug/UseDestinationValue.cs @@ -87,8 +87,8 @@ public CollectionController(object owner) cfg.CreateMap(); cfg.CreateMap(typeof(CollectionDTOController<,>), typeof(CollectionController<,,>), MemberList.None); }); - - protected override void Because_of() + [Fact] + public void Should_work() { var branchDto = new BranchDTO { ID = 51, Name = "B1" }; var orgDto = new OrganizationDTO { ID = 5, Name = "O1" }; diff --git a/src/UnitTests/Bug/WithoutPreserveReferencesSameDestination.cs b/src/UnitTests/Bug/WithoutPreserveReferencesSameDestination.cs index ed8233c48d..93207722a3 100644 --- a/src/UnitTests/Bug/WithoutPreserveReferencesSameDestination.cs +++ b/src/UnitTests/Bug/WithoutPreserveReferencesSameDestination.cs @@ -27,8 +27,8 @@ public BaseTypeDto() } protected override MapperConfiguration CreateConfiguration() => new(cfg=> cfg.CreateMap()); - - protected override void Because_of() + [Fact] + public void Should_work() { var baseType = new BaseType(); var baseTypeDto = new BaseTypeDto(); diff --git a/src/UnitTests/ConditionalMapping.cs b/src/UnitTests/ConditionalMapping.cs index 68afa900c1..26b5057a54 100644 --- a/src/UnitTests/ConditionalMapping.cs +++ b/src/UnitTests/ConditionalMapping.cs @@ -32,11 +32,8 @@ class Destination return true; })); }); - - protected override void Because_of() - { - Mapper.Map(_source, _destination); - } + [Fact] + public void Should_work() => Mapper.Map(_source, _destination); } public class When_ignoring_all_properties_with_an_inaccessible_setter_and_explicitly_implemented_member : AutoMapperSpecBase diff --git a/src/UnitTests/ExtensionMethods.cs b/src/UnitTests/ExtensionMethods.cs index cc5178e3f6..7da97be38d 100644 --- a/src/UnitTests/ExtensionMethods.cs +++ b/src/UnitTests/ExtensionMethods.cs @@ -7,6 +7,47 @@ namespace AutoMapper.UnitTests { + interface IGeneric { } + public class When_an_extension_methods_contraints_fail : NonValidatingSpecBase + { + class Source : IGeneric + { + } + class Destination + { + public int Count { get; set; } + } + protected override MapperConfiguration CreateConfiguration() => new(c => + { + c.IncludeSourceExtensionMethods(typeof(GenericExtensions)); + c.CreateMap(); + }); + [Fact] + public void It_should_fail_validation() => new Action(AssertConfigurationIsValid).ShouldThrow() + .Errors[0].UnmappedPropertyNames[0].ShouldBe(nameof(Destination.Count)); + } + public class When_an_extension_method_is_for_a_base_interface : AutoMapperSpecBase + { + class Source : IGeneric + { + } + class Destination + { + public int Value { get; set; } + } + protected override MapperConfiguration CreateConfiguration() => new(c => + { + c.IncludeSourceExtensionMethods(typeof(GenericExtensions)); + c.CreateMap(); + }); + [Fact] + public void It_should_be_used() => Map(new Source()).Value.ShouldBe(12); + } + public static class GenericExtensions + { + private static int GetValue(this IGeneric _) => 12; + private static int Count(this IGeneric _) where T : IDisposable => 12; + } public class When_an_extension_method_is_for_a_base_class : AutoMapperSpecBase { class Source diff --git a/src/UnitTests/Tests/MapperTests.cs b/src/UnitTests/Internal/MapperTests.cs similarity index 100% rename from src/UnitTests/Tests/MapperTests.cs rename to src/UnitTests/Internal/MapperTests.cs diff --git a/src/UnitTests/Tests/TypeMapFactorySpecs.cs b/src/UnitTests/Internal/TypeMapFactorySpecs.cs similarity index 100% rename from src/UnitTests/Tests/TypeMapFactorySpecs.cs rename to src/UnitTests/Internal/TypeMapFactorySpecs.cs diff --git a/src/UnitTests/MappingInheritance/PreserveReferencesWithInheritance.cs b/src/UnitTests/MappingInheritance/PreserveReferencesWithInheritance.cs index fd29626e68..34a37aec15 100644 --- a/src/UnitTests/MappingInheritance/PreserveReferencesWithInheritance.cs +++ b/src/UnitTests/MappingInheritance/PreserveReferencesWithInheritance.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Xunit; namespace AutoMapper.UnitTests { @@ -90,8 +91,8 @@ public class PreserveReferencesWithInheritance : AutoMapperSpecBase cfg.CreateMap(); cfg.CreateMap(); }); - - protected override void Because_of() + [Fact] + public void Should_work() { var field = new Source.Field { Name = "AddResult", Type = typeof(Int32) }; var @class = new Source.Class { Properties = new List { field }, Type = typeof(float) }; diff --git a/src/UnitTests/MemberResolution.cs b/src/UnitTests/MemberResolution.cs index fab3ea0bd0..90665eca04 100644 --- a/src/UnitTests/MemberResolution.cs +++ b/src/UnitTests/MemberResolution.cs @@ -1,1672 +1,1691 @@ using System; using System.Collections.Generic; -using AutoMapper.Internal.Mappers; using Shouldly; using Xunit; -using System.Reflection; -using System.Linq; +namespace AutoMapper.UnitTests.MemberResolution; -namespace AutoMapper.UnitTests +public class When_multiple_source_members_match_postfix : AutoMapperSpecBase { - namespace MemberResolution + class Order { - public class When_multiple_source_members_match : AutoMapperSpecBase - { - class Source - { - public int Value { get; set; } - public int GetValue() => 10; - public int OtherValue { get; set; } - public int GetOtherValue { get; set; } - } - class Destination - { - public int Value { get; set; } - public int OtherValue { get; set; } - } - protected override MapperConfiguration CreateConfiguration() => new(c=>c.CreateMap()); - [Fact] - public void Should_prefer_the_property() - { - var destination = Map(new Source { Value = 42, OtherValue = 42 }); - destination.Value.ShouldBe(42); - destination.OtherValue.ShouldBe(42); - } - } - public class When_mapping_derived_classes_in_arrays : AutoMapperSpecBase - { - private DtoObject[] _result; - - public class ModelObject - { - public string BaseString { get; set; } - } - - public class ModelSubObject : ModelObject - { - public string SubString { get; set; } - } + public int ProductId { get; set; } + public Product Product { get; set; } + } - public class DtoObject - { - public string BaseString { get; set; } - } + class OrderDto + { + public int ProductId { get; set; } + } + class Product + { + public int Id { get; set; } + } + protected override MapperConfiguration CreateConfiguration() => new(c => + { + c.RecognizePostfixes("Id"); + c.CreateMap(); + }); + [Fact] + public void Should_prefer_the_property() + { + var destination = Map(new Order { ProductId = 12, Product = new() { Id = 42 } }); + destination.ProductId.ShouldBe(12); + } +} +public class When_multiple_source_members_match : AutoMapperSpecBase +{ + class Source + { + public int Value { get; set; } + public int GetValue() => 10; + public int OtherValue { get; set; } + public int GetOtherValue { get; set; } + } + class Destination + { + public int Value { get; set; } + public int OtherValue { get; set; } + } + protected override MapperConfiguration CreateConfiguration() => new(c=>c.CreateMap()); + [Fact] + public void Should_prefer_the_property() + { + var destination = Map(new Source { Value = 42, OtherValue = 42 }); + destination.Value.ShouldBe(42); + destination.OtherValue.ShouldBe(42); + } +} +public class When_mapping_derived_classes_in_arrays : AutoMapperSpecBase +{ + private DtoObject[] _result; - public class DtoSubObject : DtoObject - { - public string SubString { get; set; } - } + public class ModelObject + { + public string BaseString { get; set; } + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { + public class ModelSubObject : ModelObject + { + public string SubString { get; set; } + } - cfg.CreateMap() - .Include(); + public class DtoObject + { + public string BaseString { get; set; } + } - cfg.CreateMap(); - }); + public class DtoSubObject : DtoObject + { + public string SubString { get; set; } + } - protected override void Because_of() - { - var model = new[] - { - new ModelObject {BaseString = "Base1"}, - new ModelSubObject {BaseString = "Base2", SubString = "Sub2"} - }; - _result = (DtoObject[]) Mapper.Map(model, typeof (ModelObject[]), typeof (DtoObject[])); - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { - [Fact] - public void Should_map_both_the_base_and_sub_objects() - { - _result.Length.ShouldBe(2); - _result[0].BaseString.ShouldBe("Base1"); - _result[1].BaseString.ShouldBe("Base2"); - } + cfg.CreateMap() + .Include(); - [Fact] - public void Should_map_to_the_correct_respective_dto_types() - { - _result[0].ShouldBeOfType(typeof (DtoObject)); - _result[1].ShouldBeOfType(typeof (DtoSubObject)); - } - } + cfg.CreateMap(); + }); - public class When_mapping_derived_classes : AutoMapperSpecBase + protected override void Because_of() + { + var model = new[] { - private DtoObject _result; + new ModelObject {BaseString = "Base1"}, + new ModelSubObject {BaseString = "Base2", SubString = "Sub2"} + }; + _result = (DtoObject[]) Mapper.Map(model, typeof (ModelObject[]), typeof (DtoObject[])); + } - public class ModelObject - { - public string BaseString { get; set; } - } + [Fact] + public void Should_map_both_the_base_and_sub_objects() + { + _result.Length.ShouldBe(2); + _result[0].BaseString.ShouldBe("Base1"); + _result[1].BaseString.ShouldBe("Base2"); + } - public class ModelSubObject : ModelObject - { - public string SubString { get; set; } - } + [Fact] + public void Should_map_to_the_correct_respective_dto_types() + { + _result[0].ShouldBeOfType(typeof (DtoObject)); + _result[1].ShouldBeOfType(typeof (DtoSubObject)); + } +} - public class DtoObject - { - public string BaseString { get; set; } - } +public class When_mapping_derived_classes : AutoMapperSpecBase +{ + private DtoObject _result; - public class DtoSubObject : DtoObject - { - public string SubString { get; set; } - } + public class ModelObject + { + public string BaseString { get; set; } + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap() - .Include(); + public class ModelSubObject : ModelObject + { + public string SubString { get; set; } + } - cfg.CreateMap(); - }); + public class DtoObject + { + public string BaseString { get; set; } + } - protected override void Because_of() - { - var model = new ModelSubObject { BaseString = "Base2", SubString = "Sub2" }; + public class DtoSubObject : DtoObject + { + public string SubString { get; set; } + } - _result = Mapper.Map(model); - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap() + .Include(); - [Fact] - public void Should_map_to_the_correct_dto_types() - { - _result.ShouldBeOfType(typeof(DtoSubObject)); - } - } + cfg.CreateMap(); + }); - public class When_mapping_derived_classes_from_intefaces_to_abstract : AutoMapperSpecBase - { - private DtoObject[] _result; + protected override void Because_of() + { + var model = new ModelSubObject { BaseString = "Base2", SubString = "Sub2" }; - public interface IModelObject - { - string BaseString { get; set; } - } + _result = Mapper.Map(model); + } - public class ModelSubObject : IModelObject - { - public string SubString { get; set; } - public string BaseString { get; set; } - } + [Fact] + public void Should_map_to_the_correct_dto_types() + { + _result.ShouldBeOfType(typeof(DtoSubObject)); + } +} - public abstract class DtoObject - { - public virtual string BaseString { get; set; } - } +public class When_mapping_derived_classes_from_intefaces_to_abstract : AutoMapperSpecBase +{ + private DtoObject[] _result; - public class DtoSubObject : DtoObject - { - public string SubString { get; set; } - } + public interface IModelObject + { + string BaseString { get; set; } + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap() - .Include(); + public class ModelSubObject : IModelObject + { + public string SubString { get; set; } + public string BaseString { get; set; } + } - cfg.CreateMap(); - }); + public abstract class DtoObject + { + public virtual string BaseString { get; set; } + } - protected override void Because_of() - { - var model = new IModelObject[] - { - new ModelSubObject {BaseString = "Base2", SubString = "Sub2"} - }; - _result = (DtoObject[]) Mapper.Map(model, typeof (IModelObject[]), typeof (DtoObject[])); - base.Because_of(); - } + public class DtoSubObject : DtoObject + { + public string SubString { get; set; } + } - [Fact] - public void Should_map_both_the_base_and_sub_objects() - { - _result.Length.ShouldBe(1); - _result[0].BaseString.ShouldBe("Base2"); - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap() + .Include(); - [Fact] - public void Should_map_to_the_correct_respective_dto_types() - { - _result[0].ShouldBeOfType(typeof (DtoSubObject)); - ((DtoSubObject) _result[0]).SubString.ShouldBe("Sub2"); - } - } + cfg.CreateMap(); + }); - public class When_mapping_derived_classes_as_property_of_top_object : AutoMapperSpecBase + protected override void Because_of() + { + var model = new IModelObject[] { - private DtoModel _result; + new ModelSubObject {BaseString = "Base2", SubString = "Sub2"} + }; + _result = (DtoObject[]) Mapper.Map(model, typeof (IModelObject[]), typeof (DtoObject[])); + base.Because_of(); + } - public class Model - { - public IModelObject Object { get; set; } - } + [Fact] + public void Should_map_both_the_base_and_sub_objects() + { + _result.Length.ShouldBe(1); + _result[0].BaseString.ShouldBe("Base2"); + } - public interface IModelObject - { - string BaseString { get; set; } - } + [Fact] + public void Should_map_to_the_correct_respective_dto_types() + { + _result[0].ShouldBeOfType(typeof (DtoSubObject)); + ((DtoSubObject) _result[0]).SubString.ShouldBe("Sub2"); + } +} - public class ModelSubObject : IModelObject - { - public string SubString { get; set; } - public string BaseString { get; set; } - } +public class When_mapping_derived_classes_as_property_of_top_object : AutoMapperSpecBase +{ + private DtoModel _result; - public class DtoModel - { - public DtoObject Object { get; set; } - } + public class Model + { + public IModelObject Object { get; set; } + } - public abstract class DtoObject - { - public virtual string BaseString { get; set; } - } + public interface IModelObject + { + string BaseString { get; set; } + } - public class DtoSubObject : DtoObject - { - public string SubString { get; set; } - } + public class ModelSubObject : IModelObject + { + public string SubString { get; set; } + public string BaseString { get; set; } + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); + public class DtoModel + { + public DtoObject Object { get; set; } + } - cfg.CreateMap() - .Include(); + public abstract class DtoObject + { + public virtual string BaseString { get; set; } + } - cfg.CreateMap(); - }); + public class DtoSubObject : DtoObject + { + public string SubString { get; set; } + } - [Fact] - public void Should_map_object_to_sub_object() - { - var model = new Model - { - Object = new ModelSubObject {BaseString = "Base2", SubString = "Sub2"} - }; - - _result = Mapper.Map(model); - _result.Object.ShouldNotBeNull(); - _result.Object.ShouldBeOfType(); - _result.Object.ShouldBeOfType(); - _result.Object.BaseString.ShouldBe("Base2"); - ((DtoSubObject) _result.Object).SubString.ShouldBe("Sub2"); - } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); - public class When_mapping_dto_with_only_properties : AutoMapperSpecBase - { - private ModelDto _result; + cfg.CreateMap() + .Include(); - public class ModelObject - { - public DateTime BaseDate { get; set; } - public ModelSubObject Sub { get; set; } - public ModelSubObject Sub2 { get; set; } - public ModelSubObject SubWithExtraName { get; set; } - public ModelSubObject SubMissing { get; set; } - } + cfg.CreateMap(); + }); - public class ModelSubObject + [Fact] + public void Should_map_object_to_sub_object() + { + var model = new Model { - public string ProperName { get; set; } - public ModelSubSubObject SubSub { get; set; } - } + Object = new ModelSubObject {BaseString = "Base2", SubString = "Sub2"} + }; - public class ModelSubSubObject - { - public string IAmACoolProperty { get; set; } - } + _result = Mapper.Map(model); + _result.Object.ShouldNotBeNull(); + _result.Object.ShouldBeOfType(); + _result.Object.ShouldBeOfType(); + _result.Object.BaseString.ShouldBe("Base2"); + ((DtoSubObject) _result.Object).SubString.ShouldBe("Sub2"); + } +} - public class ModelDto - { - public DateTime BaseDate { get; set; } - public string SubProperName { get; set; } - public string Sub2ProperName { get; set; } - public string SubWithExtraNameProperName { get; set; } - public string SubSubSubIAmACoolProperty { get; set; } - public string SubMissingSubSubIAmACoolProperty { get; set; } - } +public class When_mapping_dto_with_only_properties : AutoMapperSpecBase +{ + private ModelDto _result; - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { + public class ModelObject + { + public DateTime BaseDate { get; set; } + public ModelSubObject Sub { get; set; } + public ModelSubObject Sub2 { get; set; } + public ModelSubObject SubWithExtraName { get; set; } + public ModelSubObject SubMissing { get; set; } + } - cfg.CreateMap(); - }); + public class ModelSubObject + { + public string ProperName { get; set; } + public ModelSubSubObject SubSub { get; set; } + } - protected override void Because_of() - { - var model = new ModelObject - { - BaseDate = new DateTime(2007, 4, 5), - Sub = new ModelSubObject - { - ProperName = "Some name", - SubSub = new ModelSubSubObject - { - IAmACoolProperty = "Cool daddy-o" - } - }, - Sub2 = new ModelSubObject - { - ProperName = "Sub 2 name" - }, - SubWithExtraName = new ModelSubObject - { - ProperName = "Some other name" - }, - SubMissing = new ModelSubObject - { - ProperName = "I have a missing sub sub object" - } - }; - _result = Mapper.Map(model); - } + public class ModelSubSubObject + { + public string IAmACoolProperty { get; set; } + } - [Fact] - public void Should_map_item_in_first_level_of_hierarchy() - { - _result.BaseDate.ShouldBe(new DateTime(2007, 4, 5)); - } + public class ModelDto + { + public DateTime BaseDate { get; set; } + public string SubProperName { get; set; } + public string Sub2ProperName { get; set; } + public string SubWithExtraNameProperName { get; set; } + public string SubSubSubIAmACoolProperty { get; set; } + public string SubMissingSubSubIAmACoolProperty { get; set; } + } - [Fact] - public void Should_map_a_member_with_a_number() - { - _result.Sub2ProperName.ShouldBe("Sub 2 name"); - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { - [Fact] - public void Should_map_item_in_second_level_of_hierarchy() - { - _result.SubProperName.ShouldBe("Some name"); - } + cfg.CreateMap(); + }); - [Fact] - public void Should_map_item_with_more_items_in_property_name() + protected override void Because_of() + { + var model = new ModelObject + { + BaseDate = new DateTime(2007, 4, 5), + Sub = new ModelSubObject { - _result.SubWithExtraNameProperName.ShouldBe("Some other name"); - } - - [Fact] - public void Should_map_item_in_any_level_of_depth_in_the_hierarchy() + ProperName = "Some name", + SubSub = new ModelSubSubObject + { + IAmACoolProperty = "Cool daddy-o" + } + }, + Sub2 = new ModelSubObject { - _result.SubSubSubIAmACoolProperty.ShouldBe("Cool daddy-o"); - } - } - - public class When_mapping_dto_with_only_fields : AutoMapperSpecBase - { - private ModelDto _result; - - public class ModelObject + ProperName = "Sub 2 name" + }, + SubWithExtraName = new ModelSubObject { - public DateTime BaseDate; - public ModelSubObject Sub; - public ModelSubObject Sub2; - public ModelSubObject SubWithExtraName; - public ModelSubObject SubMissing; - } - - public class ModelSubObject + ProperName = "Some other name" + }, + SubMissing = new ModelSubObject { - public string ProperName; - public ModelSubSubObject SubSub; + ProperName = "I have a missing sub sub object" } + }; + _result = Mapper.Map(model); + } - public class ModelSubSubObject - { - public string IAmACoolProperty; - } + [Fact] + public void Should_map_item_in_first_level_of_hierarchy() + { + _result.BaseDate.ShouldBe(new DateTime(2007, 4, 5)); + } - public class ModelDto - { - public DateTime BaseDate; - public string SubProperName; - public string Sub2ProperName; - public string SubWithExtraNameProperName; - public string SubSubSubIAmACoolProperty; - public string SubMissingSubSubIAmACoolProperty; - } + [Fact] + public void Should_map_a_member_with_a_number() + { + _result.Sub2ProperName.ShouldBe("Sub 2 name"); + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); - }); + [Fact] + public void Should_map_item_in_second_level_of_hierarchy() + { + _result.SubProperName.ShouldBe("Some name"); + } - protected override void Because_of() - { - var model = new ModelObject - { - BaseDate = new DateTime(2007, 4, 5), - Sub = new ModelSubObject - { - ProperName = "Some name", - SubSub = new ModelSubSubObject - { - IAmACoolProperty = "Cool daddy-o" - } - }, - Sub2 = new ModelSubObject - { - ProperName = "Sub 2 name" - }, - SubWithExtraName = new ModelSubObject - { - ProperName = "Some other name" - }, - SubMissing = new ModelSubObject - { - ProperName = "I have a missing sub sub object" - } - }; - _result = Mapper.Map(model); - } + [Fact] + public void Should_map_item_with_more_items_in_property_name() + { + _result.SubWithExtraNameProperName.ShouldBe("Some other name"); + } - [Fact] - public void Should_map_item_in_first_level_of_hierarchy() - { - _result.BaseDate.ShouldBe(new DateTime(2007, 4, 5)); - } + [Fact] + public void Should_map_item_in_any_level_of_depth_in_the_hierarchy() + { + _result.SubSubSubIAmACoolProperty.ShouldBe("Cool daddy-o"); + } +} - [Fact] - public void Should_map_a_member_with_a_number() - { - _result.Sub2ProperName.ShouldBe("Sub 2 name"); - } +public class When_mapping_dto_with_only_fields : AutoMapperSpecBase +{ + private ModelDto _result; - [Fact] - public void Should_map_item_in_second_level_of_hierarchy() - { - _result.SubProperName.ShouldBe("Some name"); - } + public class ModelObject + { + public DateTime BaseDate; + public ModelSubObject Sub; + public ModelSubObject Sub2; + public ModelSubObject SubWithExtraName; + public ModelSubObject SubMissing; + } - [Fact] - public void Should_map_item_with_more_items_in_property_name() - { - _result.SubWithExtraNameProperName.ShouldBe("Some other name"); - } + public class ModelSubObject + { + public string ProperName; + public ModelSubSubObject SubSub; + } - [Fact] - public void Should_map_item_in_any_level_of_depth_in_the_hierarchy() - { - _result.SubSubSubIAmACoolProperty.ShouldBe("Cool daddy-o"); - } - } + public class ModelSubSubObject + { + public string IAmACoolProperty; + } - public class When_mapping_dto_with_fields_and_properties : AutoMapperSpecBase - { - private ModelDto _result; + public class ModelDto + { + public DateTime BaseDate; + public string SubProperName; + public string Sub2ProperName; + public string SubWithExtraNameProperName; + public string SubSubSubIAmACoolProperty; + public string SubMissingSubSubIAmACoolProperty; + } - public class ModelObject - { - public DateTime BaseDate { get; set;} - public ModelSubObject Sub; - public ModelSubObject Sub2 { get; set;} - public ModelSubObject SubWithExtraName; - public ModelSubObject SubMissing { get; set; } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + }); - public class ModelSubObject + protected override void Because_of() + { + var model = new ModelObject + { + BaseDate = new DateTime(2007, 4, 5), + Sub = new ModelSubObject { - public string ProperName { get; set;} - public ModelSubSubObject SubSub; - } - - public class ModelSubSubObject + ProperName = "Some name", + SubSub = new ModelSubSubObject + { + IAmACoolProperty = "Cool daddy-o" + } + }, + Sub2 = new ModelSubObject { - public string IAmACoolProperty { get; set;} - } - - public class ModelDto + ProperName = "Sub 2 name" + }, + SubWithExtraName = new ModelSubObject { - public DateTime BaseDate; - public string SubProperName; - public string Sub2ProperName { get; set;} - public string SubWithExtraNameProperName; - public string SubSubSubIAmACoolProperty; - public string SubMissingSubSubIAmACoolProperty { get; set;} - } - - protected override MapperConfiguration CreateConfiguration() => new(cfg => + ProperName = "Some other name" + }, + SubMissing = new ModelSubObject { - cfg.CreateMap(); - }); + ProperName = "I have a missing sub sub object" + } + }; + _result = Mapper.Map(model); + } - protected override void Because_of() - { - var model = new ModelObject - { - BaseDate = new DateTime(2007, 4, 5), - Sub = new ModelSubObject - { - ProperName = "Some name", - SubSub = new ModelSubSubObject - { - IAmACoolProperty = "Cool daddy-o" - } - }, - Sub2 = new ModelSubObject - { - ProperName = "Sub 2 name" - }, - SubWithExtraName = new ModelSubObject - { - ProperName = "Some other name" - }, - SubMissing = new ModelSubObject - { - ProperName = "I have a missing sub sub object" - } - }; - _result = Mapper.Map(model); - base.Because_of(); - } - - [Fact] - public void Should_map_item_in_first_level_of_hierarchy() - { - _result.BaseDate.ShouldBe(new DateTime(2007, 4, 5)); - } + [Fact] + public void Should_map_item_in_first_level_of_hierarchy() + { + _result.BaseDate.ShouldBe(new DateTime(2007, 4, 5)); + } - [Fact] - public void Should_map_a_member_with_a_number() - { - _result.Sub2ProperName.ShouldBe("Sub 2 name"); - } + [Fact] + public void Should_map_a_member_with_a_number() + { + _result.Sub2ProperName.ShouldBe("Sub 2 name"); + } - [Fact] - public void Should_map_item_in_second_level_of_hierarchy() - { - _result.SubProperName.ShouldBe("Some name"); - } + [Fact] + public void Should_map_item_in_second_level_of_hierarchy() + { + _result.SubProperName.ShouldBe("Some name"); + } - [Fact] - public void Should_map_item_with_more_items_in_property_name() - { - _result.SubWithExtraNameProperName.ShouldBe("Some other name"); - } + [Fact] + public void Should_map_item_with_more_items_in_property_name() + { + _result.SubWithExtraNameProperName.ShouldBe("Some other name"); + } - [Fact] - public void Should_map_item_in_any_level_of_depth_in_the_hierarchy() - { - _result.SubSubSubIAmACoolProperty.ShouldBe("Cool daddy-o"); - } - } + [Fact] + public void Should_map_item_in_any_level_of_depth_in_the_hierarchy() + { + _result.SubSubSubIAmACoolProperty.ShouldBe("Cool daddy-o"); + } +} - public class When_ignoring_a_dto_property_during_configuration : AutoMapperSpecBase - { - private IReadOnlyCollection _allTypeMaps; - private Source _source; +public class When_mapping_dto_with_fields_and_properties : AutoMapperSpecBase +{ + private ModelDto _result; - public class Source - { - public string Value { get; set; } - } + public class ModelObject + { + public DateTime BaseDate { get; set;} + public ModelSubObject Sub; + public ModelSubObject Sub2 { get; set;} + public ModelSubObject SubWithExtraName; + public ModelSubObject SubMissing { get; set; } + } - public class Destination - { - public bool Ignored - { - get { return true; } - } + public class ModelSubObject + { + public string ProperName { get; set;} + public ModelSubSubObject SubSub; + } - public string Value { get; set; } - } + public class ModelSubSubObject + { + public string IAmACoolProperty { get; set;} + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap() - .ForMember(x => x.Ignored, opt => opt.Ignore()); - }); + public class ModelDto + { + public DateTime BaseDate; + public string SubProperName; + public string Sub2ProperName { get; set;} + public string SubWithExtraNameProperName; + public string SubSubSubIAmACoolProperty; + public string SubMissingSubSubIAmACoolProperty { get; set;} + } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + }); - [Fact] - public void Should_not_report_it_as_unmapped() + protected override void Because_of() + { + var model = new ModelObject + { + BaseDate = new DateTime(2007, 4, 5), + Sub = new ModelSubObject { - foreach (var typeMap in _allTypeMaps) + ProperName = "Some name", + SubSub = new ModelSubSubObject { - typeMap.GetUnmappedPropertyNames().ShouldBeOfLength(0); + IAmACoolProperty = "Cool daddy-o" } - } - - [Fact] - public void Should_map_successfully() + }, + Sub2 = new ModelSubObject { - var destination = Mapper.Map(_source); - destination.Value.ShouldBe("foo"); - destination.Ignored.ShouldBeTrue(); - } - - protected override void Establish_context() + ProperName = "Sub 2 name" + }, + SubWithExtraName = new ModelSubObject { - _source = new Source {Value = "foo"}; - _allTypeMaps = Configuration.GetAllTypeMaps(); - } - } - - public class When_mapping_dto_with_get_methods : AutoMapperSpecBase - { - private ModelDto _result; - - public class ModelObject - { - public string GetSomeCoolValue() - { - return "Cool value"; - } - - public ModelSubObject Sub { get; set; } - } - - public class ModelSubObject + ProperName = "Some other name" + }, + SubMissing = new ModelSubObject { - public string GetSomeOtherCoolValue() - { - return "Even cooler"; - } + ProperName = "I have a missing sub sub object" } + }; + _result = Mapper.Map(model); + base.Because_of(); + } - public class ModelDto - { - public string SomeCoolValue { get; set; } - public string SubSomeOtherCoolValue { get; set; } - } + [Fact] + public void Should_map_item_in_first_level_of_hierarchy() + { + _result.BaseDate.ShouldBe(new DateTime(2007, 4, 5)); + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); - }); + [Fact] + public void Should_map_a_member_with_a_number() + { + _result.Sub2ProperName.ShouldBe("Sub 2 name"); + } - protected override void Because_of() - { - var model = new ModelObject - { - Sub = new ModelSubObject() - }; + [Fact] + public void Should_map_item_in_second_level_of_hierarchy() + { + _result.SubProperName.ShouldBe("Some name"); + } + [Fact] + public void Should_map_item_with_more_items_in_property_name() + { + _result.SubWithExtraNameProperName.ShouldBe("Some other name"); + } - _result = Mapper.Map(model); - } + [Fact] + public void Should_map_item_in_any_level_of_depth_in_the_hierarchy() + { + _result.SubSubSubIAmACoolProperty.ShouldBe("Cool daddy-o"); + } +} - [Fact] - public void Should_map_base_method_value() - { - _result.SomeCoolValue.ShouldBe("Cool value"); - } +public class When_ignoring_a_dto_property_during_configuration : AutoMapperSpecBase +{ + private IReadOnlyCollection _allTypeMaps; + private Source _source; - [Fact] - public void Should_map_second_level_method_value_off_of_property() - { - _result.SubSomeOtherCoolValue.ShouldBe("Even cooler"); - } - } + public class Source + { + public string Value { get; set; } + } - public class When_mapping_a_dto_with_names_matching_properties : AutoMapperSpecBase + public class Destination + { + public bool Ignored { - private ModelDto _result; - - public class ModelObject - { - public string SomeCoolValue() - { - return "Cool value"; - } - - public ModelSubObject Sub { get; set; } - } + get { return true; } + } - public class ModelSubObject - { - public string SomeOtherCoolValue() - { - return "Even cooler"; - } - } + public string Value { get; set; } + } - public class ModelDto - { - public string SomeCoolValue { get; set; } - public string SubSomeOtherCoolValue { get; set; } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap() + .ForMember(x => x.Ignored, opt => opt.Ignore()); + }); - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); - }); - protected override void Because_of() - { - var model = new ModelObject - { - Sub = new ModelSubObject() - }; + [Fact] + public void Should_not_report_it_as_unmapped() + { + foreach (var typeMap in _allTypeMaps) + { + typeMap.GetUnmappedPropertyNames().ShouldBeOfLength(0); + } + } + [Fact] + public void Should_map_successfully() + { + var destination = Mapper.Map(_source); + destination.Value.ShouldBe("foo"); + destination.Ignored.ShouldBeTrue(); + } - _result = Mapper.Map(model); - } + protected override void Establish_context() + { + _source = new Source {Value = "foo"}; + _allTypeMaps = Configuration.GetAllTypeMaps(); + } +} - [Fact] - public void Should_map_base_method_value() - { - _result.SomeCoolValue.ShouldBe("Cool value"); - } +public class When_mapping_dto_with_get_methods : AutoMapperSpecBase +{ + private ModelDto _result; - [Fact] - public void Should_map_second_level_method_value_off_of_property() - { - _result.SubSomeOtherCoolValue.ShouldBe("Even cooler"); - } + public class ModelObject + { + public string GetSomeCoolValue() + { + return "Cool value"; } - public class When_mapping_with_a_dto_subtype : AutoMapperSpecBase + public ModelSubObject Sub { get; set; } + } + + public class ModelSubObject + { + public string GetSomeOtherCoolValue() { - private ModelDto _result; + return "Even cooler"; + } + } - public class ModelObject - { - public ModelSubObject Sub { get; set; } - } + public class ModelDto + { + public string SomeCoolValue { get; set; } + public string SubSomeOtherCoolValue { get; set; } + } - public class ModelSubObject - { - public string SomeValue { get; set; } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + }); - public class ModelDto - { - public ModelSubDto Sub { get; set; } - } + protected override void Because_of() + { + var model = new ModelObject + { + Sub = new ModelSubObject() + }; - public class ModelSubDto - { - public string SomeValue { get; set; } - } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); - cfg.CreateMap(); + _result = Mapper.Map(model); + } - }); + [Fact] + public void Should_map_base_method_value() + { + _result.SomeCoolValue.ShouldBe("Cool value"); + } - protected override void Because_of() - { - var model = new ModelObject - { - Sub = new ModelSubObject - { - SomeValue = "Some value" - } - }; + [Fact] + public void Should_map_second_level_method_value_off_of_property() + { + _result.SubSomeOtherCoolValue.ShouldBe("Even cooler"); + } +} - _result = Mapper.Map(model); - } +public class When_mapping_a_dto_with_names_matching_properties : AutoMapperSpecBase +{ + private ModelDto _result; - [Fact] - public void Should_map_the_model_sub_type_to_the_dto_sub_type() - { - _result.Sub.ShouldNotBeNull(); - _result.Sub.SomeValue.ShouldBe("Some value"); - } + public class ModelObject + { + public string SomeCoolValue() + { + return "Cool value"; } - public class When_mapping_a_dto_with_a_set_only_property_and_a_get_method : AutoMapperSpecBase + public ModelSubObject Sub { get; set; } + } + + public class ModelSubObject + { + public string SomeOtherCoolValue() { - private ModelDto _result; + return "Even cooler"; + } + } - public class ModelDto - { - public int SomeValue { get; set; } - } + public class ModelDto + { + public string SomeCoolValue { get; set; } + public string SubSomeOtherCoolValue { get; set; } + } - public class ModelObject - { - private int _someValue; + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + }); - public int SomeValue - { - set { _someValue = value; } - } + protected override void Because_of() + { + var model = new ModelObject + { + Sub = new ModelSubObject() + }; - public int GetSomeValue() - { - return _someValue; - } - } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); + _result = Mapper.Map(model); + } - }); + [Fact] + public void Should_map_base_method_value() + { + _result.SomeCoolValue.ShouldBe("Cool value"); + } - protected override void Because_of() - { - var model = new ModelObject(); - model.SomeValue = 46; + [Fact] + public void Should_map_second_level_method_value_off_of_property() + { + _result.SubSomeOtherCoolValue.ShouldBe("Even cooler"); + } +} - _result = Mapper.Map(model); - } +public class When_mapping_with_a_dto_subtype : AutoMapperSpecBase +{ + private ModelDto _result; - [Fact] - public void Should_map_the_get_method_to_the_dto() - { - _result.SomeValue.ShouldBe(46); - } - } + public class ModelObject + { + public ModelSubObject Sub { get; set; } + } - public class When_mapping_using_a_custom_member_mappings : AutoMapperSpecBase - { - private ModelDto _result; + public class ModelSubObject + { + public string SomeValue { get; set; } + } - public class ModelObject - { - public int Blarg { get; set; } - public string MoreBlarg { get; set; } + public class ModelDto + { + public ModelSubDto Sub { get; set; } + } - public int SomeMethodToGetMoreBlarg() - { - return 45; - } + public class ModelSubDto + { + public string SomeValue { get; set; } + } - public string SomeValue { get; set; } - public ModelSubObject SomeWeirdSubObject { get; set; } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + cfg.CreateMap(); - public string IAmSomeMethod() - { - return "I am some method"; - } - } + }); - public class ModelSubObject + protected override void Because_of() + { + var model = new ModelObject + { + Sub = new ModelSubObject { - public int Narf { get; set; } - public ModelSubSubObject SubSub { get; set; } - - public string SomeSubValue() - { - return "I am some sub value"; - } + SomeValue = "Some value" } + }; - public class ModelSubSubObject - { - public int Norf { get; set; } + _result = Mapper.Map(model); + } - public string SomeSubSubValue() - { - return "I am some sub sub value"; - } - } + [Fact] + public void Should_map_the_model_sub_type_to_the_dto_sub_type() + { + _result.Sub.ShouldNotBeNull(); + _result.Sub.SomeValue.ShouldBe("Some value"); + } +} - public class ModelDto - { - public int Splorg { get; set; } - public string SomeValue { get; set; } - public string SomeMethod { get; set; } - public int SubNarf { get; set; } - public string SubValue { get; set; } - public int GrandChildInt { get; set; } - public string GrandChildString { get; set; } - public int BlargPlus3 { get; set; } - public int BlargMinus2 { get; set; } - public int MoreBlarg { get; set; } - } +public class When_mapping_a_dto_with_a_set_only_property_and_a_get_method : AutoMapperSpecBase +{ + private ModelDto _result; - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg - .CreateMap() - .ForMember(dto => dto.Splorg, opt => opt.MapFrom(m => m.Blarg)) - .ForMember(dto => dto.SomeMethod, opt => opt.MapFrom(m => m.IAmSomeMethod())) - .ForMember(dto => dto.SubNarf, opt => opt.MapFrom(m => m.SomeWeirdSubObject.Narf)) - .ForMember(dto => dto.SubValue, opt => opt.MapFrom(m => m.SomeWeirdSubObject.SomeSubValue())) - .ForMember(dto => dto.GrandChildInt, opt => opt.MapFrom(m => m.SomeWeirdSubObject.SubSub.Norf)) - .ForMember(dto => dto.GrandChildString, - opt => opt.MapFrom(m => m.SomeWeirdSubObject.SubSub.SomeSubSubValue())) - .ForMember(dto => dto.MoreBlarg, opt => opt.MapFrom(m => m.SomeMethodToGetMoreBlarg())) - .ForMember(dto => dto.BlargPlus3, opt => opt.MapFrom(m => m.Blarg.Plus(3))) - .ForMember(dto => dto.BlargMinus2, opt => opt.MapFrom(m => m.Blarg - 2)); - - }); - - protected override void Because_of() - { - var model = new ModelObject - { - Blarg = 10, - SomeValue = "Some value", - SomeWeirdSubObject = new ModelSubObject - { - Narf = 5, - SubSub = new ModelSubSubObject - { - Norf = 15 - } - }, - MoreBlarg = "adsfdsaf" - }; - _result = Mapper.Map(model); - } + public class ModelDto + { + public int SomeValue { get; set; } + } - [Fact] - public void Should_preserve_the_existing_mapping() - { - _result.SomeValue.ShouldBe("Some value"); - } + public class ModelObject + { + private int _someValue; - [Fact] - public void Should_map_top_level_properties() - { - _result.Splorg.ShouldBe(10); - } + public int SomeValue + { + set { _someValue = value; } + } - [Fact] - public void Should_map_methods_results() - { - _result.SomeMethod.ShouldBe("I am some method"); - } + public int GetSomeValue() + { + return _someValue; + } + } - [Fact] - public void Should_map_children_properties() - { - _result.SubNarf.ShouldBe(5); - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); - [Fact] - public void Should_map_children_methods() - { - _result.SubValue.ShouldBe("I am some sub value"); - } + }); - [Fact] - public void Should_map_grandchildren_properties() - { - _result.GrandChildInt.ShouldBe(15); - } + protected override void Because_of() + { + var model = new ModelObject(); + model.SomeValue = 46; - [Fact] - public void Should_map_grandchildren_methods() - { - _result.GrandChildString.ShouldBe("I am some sub sub value"); - } + _result = Mapper.Map(model); + } - [Fact] - public void Should_map_blarg_plus_three_using_extension_method() - { - _result.BlargPlus3.ShouldBe(13); - } + [Fact] + public void Should_map_the_get_method_to_the_dto() + { + _result.SomeValue.ShouldBe(46); + } +} - [Fact] - public void Should_map_blarg_minus_2_using_lambda() - { - _result.BlargMinus2.ShouldBe(8); - } +public class When_mapping_using_a_custom_member_mappings : AutoMapperSpecBase +{ + private ModelDto _result; - [Fact] - public void Should_override_existing_matches_for_new_mappings() - { - _result.MoreBlarg.ShouldBe(45); - } + public class ModelObject + { + public int Blarg { get; set; } + public string MoreBlarg { get; set; } + + public int SomeMethodToGetMoreBlarg() + { + return 45; } - public class When_mapping_using_custom_member_mappings_without_generics : AutoMapperSpecBase + public string SomeValue { get; set; } + public ModelSubObject SomeWeirdSubObject { get; set; } + + public string IAmSomeMethod() { - private OrderDTO _result; + return "I am some method"; + } + } - public class Order - { - public int Id { get; set; } - public string Status { get; set; } - public string Customer { get; set; } - public string ShippingCode { get; set; } - public string Zip { get; set; } - } + public class ModelSubObject + { + public int Narf { get; set; } + public ModelSubSubObject SubSub { get; set; } - public class OrderDTO - { - public int Id { get; set; } - public string CurrentState { get; set; } - public string Contact { get; set; } - public string Tracking { get; set; } - public string Postal { get; set; } - } + public string SomeSubValue() + { + return "I am some sub value"; + } + } - public class StringCAPS : IMemberValueResolver - { - public string Resolve(object s, object d, string source, string dest, ResolutionContext context) - { - return source.ToUpper(); - } - } + public class ModelSubSubObject + { + public int Norf { get; set; } + + public string SomeSubSubValue() + { + return "I am some sub sub value"; + } + } - public class StringLower : IMemberValueResolver + public class ModelDto + { + public int Splorg { get; set; } + public string SomeValue { get; set; } + public string SomeMethod { get; set; } + public int SubNarf { get; set; } + public string SubValue { get; set; } + public int GrandChildInt { get; set; } + public string GrandChildString { get; set; } + public int BlargPlus3 { get; set; } + public int BlargMinus2 { get; set; } + public int MoreBlarg { get; set; } + } + + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg + .CreateMap() + .ForMember(dto => dto.Splorg, opt => opt.MapFrom(m => m.Blarg)) + .ForMember(dto => dto.SomeMethod, opt => opt.MapFrom(m => m.IAmSomeMethod())) + .ForMember(dto => dto.SubNarf, opt => opt.MapFrom(m => m.SomeWeirdSubObject.Narf)) + .ForMember(dto => dto.SubValue, opt => opt.MapFrom(m => m.SomeWeirdSubObject.SomeSubValue())) + .ForMember(dto => dto.GrandChildInt, opt => opt.MapFrom(m => m.SomeWeirdSubObject.SubSub.Norf)) + .ForMember(dto => dto.GrandChildString, + opt => opt.MapFrom(m => m.SomeWeirdSubObject.SubSub.SomeSubSubValue())) + .ForMember(dto => dto.MoreBlarg, opt => opt.MapFrom(m => m.SomeMethodToGetMoreBlarg())) + .ForMember(dto => dto.BlargPlus3, opt => opt.MapFrom(m => m.Blarg.Plus(3))) + .ForMember(dto => dto.BlargMinus2, opt => opt.MapFrom(m => m.Blarg - 2)); + + }); + + protected override void Because_of() + { + var model = new ModelObject + { + Blarg = 10, + SomeValue = "Some value", + SomeWeirdSubObject = new ModelSubObject { - public string Resolve(object s, object d, string source, string dest, ResolutionContext context) + Narf = 5, + SubSub = new ModelSubSubObject { - return source.ToLower(); + Norf = 15 } - } + }, + MoreBlarg = "adsfdsaf" + }; + _result = Mapper.Map(model); + } - public class StringPadder : IMemberValueResolver - { - private readonly int _desiredLength; + [Fact] + public void Should_preserve_the_existing_mapping() + { + _result.SomeValue.ShouldBe("Some value"); + } - public StringPadder(int desiredLength) - { - _desiredLength = desiredLength; - } + [Fact] + public void Should_map_top_level_properties() + { + _result.Splorg.ShouldBe(10); + } - public string Resolve(object s, object d, string source, string dest, ResolutionContext context) - { - return source.PadLeft(_desiredLength); - } - } + [Fact] + public void Should_map_methods_results() + { + _result.SomeMethod.ShouldBe("I am some method"); + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(typeof (Order), typeof (OrderDTO)) - .ForMember("CurrentState", map => map.MapFrom("Status")) - .ForMember("Contact", map => map.MapFrom(new StringCAPS(), "Customer")) - .ForMember("Tracking", map => map.MapFrom(typeof (StringLower), "ShippingCode")) - .ForMember("Postal", map => map.MapFrom(new StringPadder(6), "Zip")); + [Fact] + public void Should_map_children_properties() + { + _result.SubNarf.ShouldBe(5); + } - }); + [Fact] + public void Should_map_children_methods() + { + _result.SubValue.ShouldBe("I am some sub value"); + } - protected override void Because_of() - { - var order = new Order - { - Id = 7, - Status = "Pending", - Customer = "Buster", - ShippingCode = "AbcxY23", - Zip = "XYZ" - }; - _result = Mapper.Map(order); - } + [Fact] + public void Should_map_grandchildren_properties() + { + _result.GrandChildInt.ShouldBe(15); + } - [Fact] - public void Should_preserve_existing_mapping() - { - _result.Id.ShouldBe(7); - } + [Fact] + public void Should_map_grandchildren_methods() + { + _result.GrandChildString.ShouldBe("I am some sub sub value"); + } - [Fact] - public void Should_support_custom_source_member() - { - _result.CurrentState.ShouldBe("Pending"); - } + [Fact] + public void Should_map_blarg_plus_three_using_extension_method() + { + _result.BlargPlus3.ShouldBe(13); + } - [Fact] - public void Should_support_custom_resolver_on_custom_source_member() - { - _result.Contact.ShouldBe("BUSTER"); - } + [Fact] + public void Should_map_blarg_minus_2_using_lambda() + { + _result.BlargMinus2.ShouldBe(8); + } - [Fact] - public void Should_support_custom_resolver_by_type_on_custom_source_member() - { - _result.Tracking.ShouldBe("abcxy23"); - } + [Fact] + public void Should_override_existing_matches_for_new_mappings() + { + _result.MoreBlarg.ShouldBe(45); + } +} - [Fact] - public void Should_support_custom_resolver_by_generic_type_with_constructor_on_custom_source_member() - { - _result.Postal.ShouldBe(" XYZ"); - } +public class When_mapping_using_custom_member_mappings_without_generics : AutoMapperSpecBase +{ + private OrderDTO _result; + + public class Order + { + public int Id { get; set; } + public string Status { get; set; } + public string Customer { get; set; } + public string ShippingCode { get; set; } + public string Zip { get; set; } + } + public class OrderDTO + { + public int Id { get; set; } + public string CurrentState { get; set; } + public string Contact { get; set; } + public string Tracking { get; set; } + public string Postal { get; set; } + } + + public class StringCAPS : IMemberValueResolver + { + public string Resolve(object s, object d, string source, string dest, ResolutionContext context) + { + return source.ToUpper(); } - - public class When_mapping_to_a_top_level_camelCased_destination_member : AutoMapperSpecBase + } + + public class StringLower : IMemberValueResolver + { + public string Resolve(object s, object d, string source, string dest, ResolutionContext context) { - private Destination _result; + return source.ToLower(); + } + } - public class Source - { - public int SomeValueWithPascalName { get; set; } - } + public class StringPadder : IMemberValueResolver + { + private readonly int _desiredLength; - public class Destination - { - public int someValueWithPascalName { get; set; } - } + public StringPadder(int desiredLength) + { + _desiredLength = desiredLength; + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); - }); + public string Resolve(object s, object d, string source, string dest, ResolutionContext context) + { + return source.PadLeft(_desiredLength); + } + } - protected override void Because_of() - { - var source = new Source {SomeValueWithPascalName = 5}; - _result = Mapper.Map(source); - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(typeof (Order), typeof (OrderDTO)) + .ForMember("CurrentState", map => map.MapFrom("Status")) + .ForMember("Contact", map => map.MapFrom(new StringCAPS(), "Customer")) + .ForMember("Tracking", map => map.MapFrom(typeof (StringLower), "ShippingCode")) + .ForMember("Postal", map => map.MapFrom(new StringPadder(6), "Zip")); - [Fact] - public void Should_match_to_PascalCased_source_member() - { - _result.someValueWithPascalName.ShouldBe(5); - } - } + }); - public class When_mapping_to_a_self_referential_object : AutoMapperSpecBase + protected override void Because_of() + { + var order = new Order { - private CategoryDto _result; + Id = 7, + Status = "Pending", + Customer = "Buster", + ShippingCode = "AbcxY23", + Zip = "XYZ" + }; + _result = Mapper.Map(order); + } - public class Category - { - public string Name { get; set; } - public IList Children { get; set; } - } + [Fact] + public void Should_preserve_existing_mapping() + { + _result.Id.ShouldBe(7); + } - public class CategoryDto - { - public string Name { get; set; } - public CategoryDto[] Children { get; set; } - } + [Fact] + public void Should_support_custom_source_member() + { + _result.CurrentState.ShouldBe("Pending"); + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); - }); + [Fact] + public void Should_support_custom_resolver_on_custom_source_member() + { + _result.Contact.ShouldBe("BUSTER"); + } - protected override void Because_of() - { - var category = new Category - { - Name = "Grandparent", - Children = new List() - { - new Category { Name = "Parent 1", Children = new List() - { - new Category { Name = "Child 1"}, - new Category { Name = "Child 2"}, - new Category { Name = "Child 3"}, - }}, - new Category { Name = "Parent 2", Children = new List() - { - new Category { Name = "Child 4"}, - new Category { Name = "Child 5"}, - new Category { Name = "Child 6"}, - new Category { Name = "Child 7"}, - }}, - } - }; - _result = Mapper.Map(category); - } + [Fact] + public void Should_support_custom_resolver_by_type_on_custom_source_member() + { + _result.Tracking.ShouldBe("abcxy23"); + } - [Fact] - public void Should_resolve_any_level_of_hierarchies() - { - _result.Name.ShouldBe("Grandparent"); - _result.Children.Length.ShouldBe(2); - _result.Children[0].Children.Length.ShouldBe(3); - _result.Children[1].Children.Length.ShouldBe(4); - } - } + [Fact] + public void Should_support_custom_resolver_by_generic_type_with_constructor_on_custom_source_member() + { + _result.Postal.ShouldBe(" XYZ"); + } + +} - public class When_mapping_to_types_in_a_non_generic_manner : AutoMapperSpecBase - { - private Destination _result; +public class When_mapping_to_a_top_level_camelCased_destination_member : AutoMapperSpecBase +{ + private Destination _result; - public class Source - { - public int Value { get; set; } - } + public class Source + { + public int SomeValueWithPascalName { get; set; } + } - public class Destination - { - public int Value { get; set; } - } + public class Destination + { + public int someValueWithPascalName { get; set; } + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(typeof (Source), typeof (Destination)); - }); + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + }); - protected override void Because_of() - { - _result = Mapper.Map(new Source {Value = 5}); - } + protected override void Because_of() + { + var source = new Source {SomeValueWithPascalName = 5}; + _result = Mapper.Map(source); + } - [Fact] - public void Should_allow_for_basic_mapping() - { - _result.Value.ShouldBe(5); - } - } + [Fact] + public void Should_match_to_PascalCased_source_member() + { + _result.someValueWithPascalName.ShouldBe(5); + } +} - public class When_matching_source_and_destination_members_with_underscored_members : AutoMapperSpecBase - { - private Destination _destination; +public class When_mapping_to_a_self_referential_object : AutoMapperSpecBase +{ + private CategoryDto _result; - public class Source - { - public SubSource some_source { get; set; } - } + public class Category + { + public string Name { get; set; } + public IList Children { get; set; } + } - public class SubSource - { - public int value { get; set; } - } + public class CategoryDto + { + public string Name { get; set; } + public CategoryDto[] Children { get; set; } + } - public class Destination - { - public int some_source_value { get; set; } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + }); - protected override MapperConfiguration CreateConfiguration() => new(cfg => + protected override void Because_of() + { + var category = new Category + { + Name = "Grandparent", + Children = new List() { - cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); - cfg.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention(); - cfg.CreateMap(); - }); + new Category { Name = "Parent 1", Children = new List() + { + new Category { Name = "Child 1"}, + new Category { Name = "Child 2"}, + new Category { Name = "Child 3"}, + }}, + new Category { Name = "Parent 2", Children = new List() + { + new Category { Name = "Child 4"}, + new Category { Name = "Child 5"}, + new Category { Name = "Child 6"}, + new Category { Name = "Child 7"}, + }}, + } + }; + _result = Mapper.Map(category); + } - protected override void Because_of() - { - _destination = Mapper.Map(new Source {some_source = new SubSource {value = 8}}); - } + [Fact] + public void Should_resolve_any_level_of_hierarchies() + { + _result.Name.ShouldBe("Grandparent"); + _result.Children.Length.ShouldBe(2); + _result.Children[0].Children.Length.ShouldBe(3); + _result.Children[1].Children.Length.ShouldBe(4); + } +} + +public class When_mapping_to_types_in_a_non_generic_manner : AutoMapperSpecBase +{ + private Destination _result; - [Fact] - public void Should_use_underscores_as_tokenizers_to_flatten() - { - _destination.some_source_value.ShouldBe(8); - } - } + public class Source + { + public int Value { get; set; } + } - public class When_source_members_configured_in_a_root_profile_contain_prefixes : AutoMapperSpecBase - { - private Destination _destination; + public class Destination + { + public int Value { get; set; } + } - public class Source - { - public int FooValue { get; set; } - public int GetOtherValue() - { - return 10; - } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(typeof (Source), typeof (Destination)); + }); - public class Destination - { - public int Value { get; set; } - public int OtherValue { get; set; } - } + protected override void Because_of() + { + _result = Mapper.Map(new Source {Value = 5}); + } - public class ChildProfile : Profile - { - public ChildProfile() - { - CreateMap(); - } - } + [Fact] + public void Should_allow_for_basic_mapping() + { + _result.Value.ShouldBe(5); + } +} - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.RecognizePrefixes("Foo"); - cfg.AddProfile(); - }); +public class When_matching_source_and_destination_members_with_underscored_members : AutoMapperSpecBase +{ + private Destination _destination; - protected override void Because_of() - { - _destination = Mapper.Map(new Source { FooValue = 5 }); - } + public class Source + { + public SubSource some_source { get; set; } + } - [Fact] - public void Registered_prefixes_ignored() - { - _destination.Value.ShouldBe(5); - } + public class SubSource + { + public int value { get; set; } + } - [Fact] - public void Default_prefix_included() - { - _destination.OtherValue.ShouldBe(10); - } + public class Destination + { + public int some_source_value { get; set; } + } + + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); + cfg.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention(); + cfg.CreateMap(); + }); + + protected override void Because_of() + { + _destination = Mapper.Map(new Source {some_source = new SubSource {value = 8}}); + } + + [Fact] + public void Should_use_underscores_as_tokenizers_to_flatten() + { + _destination.some_source_value.ShouldBe(8); + } +} + +public class When_source_members_configured_in_a_root_profile_contain_prefixes : AutoMapperSpecBase +{ + private Destination _destination; + + public class Source + { + public int FooValue { get; set; } + public int GetOtherValue() + { + return 10; } + } + + public class Destination + { + public int Value { get; set; } + public int OtherValue { get; set; } + } - public class When_source_members_contain_prefixes : AutoMapperSpecBase + public class ChildProfile : Profile + { + public ChildProfile() { - private Destination _destination; + CreateMap(); + } + } - public class Source - { - public int FooValue { get; set; } - public int GetOtherValue() - { - return 10; - } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.RecognizePrefixes("Foo"); + cfg.AddProfile(); + }); - public class Destination - { - public int Value { get; set; } - public int OtherValue { get; set; } - } + protected override void Because_of() + { + _destination = Mapper.Map(new Source { FooValue = 5 }); + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.RecognizePrefixes("Foo"); - cfg.CreateMap(); - }); + [Fact] + public void Registered_prefixes_ignored() + { + _destination.Value.ShouldBe(5); + } - protected override void Because_of() - { - _destination = Mapper.Map(new Source {FooValue = 5}); - } + [Fact] + public void Default_prefix_included() + { + _destination.OtherValue.ShouldBe(10); + } +} - [Fact] - public void Registered_prefixes_ignored() - { - _destination.Value.ShouldBe(5); - } +public class When_source_members_contain_prefixes : AutoMapperSpecBase +{ + private Destination _destination; - [Fact] - public void Default_prefix_included() - { - _destination.OtherValue.ShouldBe(10); - } + public class Source + { + public int FooValue { get; set; } + public int GetOtherValue() + { + return 10; } + } + public class Destination + { + public int Value { get; set; } + public int OtherValue { get; set; } + } - public class When_source_members_contain_prefixes_with_lowercase : AutoMapperSpecBase - { - private Destination _destination; - - public class Source - { - public int fooValue { get; set; } - public int GetOtherValue() - { - return 10; - } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.RecognizePrefixes("Foo"); + cfg.CreateMap(); + }); - public class Destination - { - public int Value { get; set; } - public int OtherValue { get; set; } - } + protected override void Because_of() + { + _destination = Mapper.Map(new Source {FooValue = 5}); + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.RecognizePrefixes("Foo"); - cfg.CreateMap(); - }); + [Fact] + public void Registered_prefixes_ignored() + { + _destination.Value.ShouldBe(5); + } - protected override void Because_of() - { - _destination = Mapper.Map(new Source { fooValue = 5 }); - } + [Fact] + public void Default_prefix_included() + { + _destination.OtherValue.ShouldBe(10); + } +} - [Fact] - public void Registered_prefixes_ignored() - { - _destination.Value.ShouldBe(5); - } - [Fact] - public void Default_prefix_included() - { - _destination.OtherValue.ShouldBe(10); - } - } +public class When_source_members_contain_prefixes_with_lowercase : AutoMapperSpecBase +{ + private Destination _destination; - public class When_source_members_contain_postfixes_with_lowercase : AutoMapperSpecBase + public class Source + { + public int fooValue { get; set; } + public int GetOtherValue() { - private Destination _destination; + return 10; + } + } - public class Source - { - public int Valuefoo { get; set; } - public int GetOtherValue() - { - return 10; - } - } + public class Destination + { + public int Value { get; set; } + public int OtherValue { get; set; } + } - public class Destination - { - public int Value { get; set; } - public int OtherValue { get; set; } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.RecognizePrefixes("Foo"); + cfg.CreateMap(); + }); - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.RecognizePostfixes("Foo"); - cfg.CreateMap(); - }); + protected override void Because_of() + { + _destination = Mapper.Map(new Source { fooValue = 5 }); + } - protected override void Because_of() - { - _destination = Mapper.Map(new Source { Valuefoo = 5 }); - } + [Fact] + public void Registered_prefixes_ignored() + { + _destination.Value.ShouldBe(5); + } - [Fact] - public void Registered_prefixes_ignored() - { - _destination.Value.ShouldBe(5); - } + [Fact] + public void Default_prefix_included() + { + _destination.OtherValue.ShouldBe(10); + } +} - [Fact] - public void Default_prefix_included() - { - _destination.OtherValue.ShouldBe(10); - } - } +public class When_source_members_contain_postfixes_with_lowercase : AutoMapperSpecBase +{ + private Destination _destination; - public class When_source_members_configured_in_a_root_profile_contain_postfixes_and_prefixes : AutoMapperSpecBase + public class Source + { + public int Valuefoo { get; set; } + public int GetOtherValue() { - private Destination _destination; + return 10; + } + } - public class Source - { - public int FooValueBar { get; set; } - public int GetOtherValue() - { - return 10; - } - } + public class Destination + { + public int Value { get; set; } + public int OtherValue { get; set; } + } - public class Destination - { - public int Value { get; set; } - public int OtherValue { get; set; } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.RecognizePostfixes("Foo"); + cfg.CreateMap(); + }); - public class ChildProfile : Profile - { - public ChildProfile() - { - CreateMap(); - } - } + protected override void Because_of() + { + _destination = Mapper.Map(new Source { Valuefoo = 5 }); + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.RecognizePrefixes("Foo"); - cfg.RecognizePostfixes("Bar"); - cfg.AddProfile(); - }); + [Fact] + public void Registered_prefixes_ignored() + { + _destination.Value.ShouldBe(5); + } - protected override void Because_of() - { - _destination = Mapper.Map(new Source { FooValueBar = 5 }); - } + [Fact] + public void Default_prefix_included() + { + _destination.OtherValue.ShouldBe(10); + } +} - [Fact] - public void Registered_prefixes_ignored() - { - _destination.Value.ShouldBe(5); - } +public class When_source_members_configured_in_a_root_profile_contain_postfixes_and_prefixes : AutoMapperSpecBase +{ + private Destination _destination; - [Fact] - public void Default_prefix_included() - { - _destination.OtherValue.ShouldBe(10); - } + public class Source + { + public int FooValueBar { get; set; } + public int GetOtherValue() + { + return 10; } + } + public class Destination + { + public int Value { get; set; } + public int OtherValue { get; set; } + } - public class When_source_members_contain_postfixes_and_prefixes : AutoMapperSpecBase + public class ChildProfile : Profile + { + public ChildProfile() { - private Destination _destination; + CreateMap(); + } + } - public class Source - { - public int FooValueBar { get; set; } - public int GetOtherValue() - { - return 10; - } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.RecognizePrefixes("Foo"); + cfg.RecognizePostfixes("Bar"); + cfg.AddProfile(); + }); - public class Destination - { - public int Value { get; set; } - public int OtherValue { get; set; } - } + protected override void Because_of() + { + _destination = Mapper.Map(new Source { FooValueBar = 5 }); + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.RecognizePrefixes("Foo"); - cfg.RecognizePostfixes("Bar"); - cfg.CreateMap(); - }); + [Fact] + public void Registered_prefixes_ignored() + { + _destination.Value.ShouldBe(5); + } - protected override void Because_of() - { - _destination = Mapper.Map(new Source { FooValueBar = 5 }); - } + [Fact] + public void Default_prefix_included() + { + _destination.OtherValue.ShouldBe(10); + } +} - [Fact] - public void Registered_prefixes_ignored() - { - _destination.Value.ShouldBe(5); - } - [Fact] - public void Default_prefix_included() - { - _destination.OtherValue.ShouldBe(10); - } - } +public class When_source_members_contain_postfixes_and_prefixes : AutoMapperSpecBase +{ + private Destination _destination; - public class When_source_member_names_match_with_underscores : AutoMapperSpecBase + public class Source + { + public int FooValueBar { get; set; } + public int GetOtherValue() { - private Destination _destination; + return 10; + } + } - public class Source - { - public int I_amaCraAZZEE____Name { get; set; } - } + public class Destination + { + public int Value { get; set; } + public int OtherValue { get; set; } + } - public class Destination - { - public int I_amaCraAZZEE____Name { get; set; } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.RecognizePrefixes("Foo"); + cfg.RecognizePostfixes("Bar"); + cfg.CreateMap(); + }); - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); - }); + protected override void Because_of() + { + _destination = Mapper.Map(new Source { FooValueBar = 5 }); + } - protected override void Because_of() - { - _destination = Mapper.Map(new Source {I_amaCraAZZEE____Name = 5}); - } + [Fact] + public void Registered_prefixes_ignored() + { + _destination.Value.ShouldBe(5); + } - [Fact] - public void Should_match_based_on_name() - { - _destination.I_amaCraAZZEE____Name.ShouldBe(5); - } - } + [Fact] + public void Default_prefix_included() + { + _destination.OtherValue.ShouldBe(10); + } +} - public class When_recognizing_explicit_member_aliases : AutoMapperSpecBase - { - private Destination _destination; +public class When_source_member_names_match_with_underscores : AutoMapperSpecBase +{ + private Destination _destination; - public class Source - { - public int Foo { get; set; } - } + public class Source + { + public int I_amaCraAZZEE____Name { get; set; } + } - public class Destination - { - public int Bar { get; set; } - } + public class Destination + { + public int I_amaCraAZZEE____Name { get; set; } + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.ReplaceMemberName("Foo", "Bar"); - cfg.CreateMap(); - }); + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(); + }); - protected override void Because_of() - { - _destination = Mapper.Map(new Source {Foo = 5}); - } + protected override void Because_of() + { + _destination = Mapper.Map(new Source {I_amaCraAZZEE____Name = 5}); + } - [Fact] - public void Members_that_match_alias_should_be_matched() - { - _destination.Bar.ShouldBe(5); - } - } + [Fact] + public void Should_match_based_on_name() + { + _destination.I_amaCraAZZEE____Name.ShouldBe(5); + } +} - public class When_destination_members_contain_prefixes : AutoMapperSpecBase - { - private Destination _destination; +public class When_recognizing_explicit_member_aliases : AutoMapperSpecBase +{ + private Destination _destination; - public class Source - { - public int Value { get; set; } - public int Value2 { get; set; } - } + public class Source + { + public int Foo { get; set; } + } - public class Destination - { - public int FooValue { get; set; } - public int BarValue2 { get; set; } - } + public class Destination + { + public int Bar { get; set; } + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.RecognizeDestinationPrefixes("Foo", "Bar"); - cfg.CreateMap(); - }); + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.ReplaceMemberName("Foo", "Bar"); + cfg.CreateMap(); + }); - protected override void Because_of() - { - _destination = Mapper.Map(new Source { Value = 5, Value2 = 10 }); - } + protected override void Because_of() + { + _destination = Mapper.Map(new Source {Foo = 5}); + } - [Fact] - public void Registered_prefixes_ignored() - { - _destination.FooValue.ShouldBe(5); - _destination.BarValue2.ShouldBe(10); - } - } + [Fact] + public void Members_that_match_alias_should_be_matched() + { + _destination.Bar.ShouldBe(5); + } +} + +public class When_destination_members_contain_prefixes : AutoMapperSpecBase +{ + private Destination _destination; + public class Source + { + public int Value { get; set; } + public int Value2 { get; set; } } - public class When_destination_type_has_private_members : AutoMapperSpecBase + public class Destination { - private IDestination _destination; + public int FooValue { get; set; } + public int BarValue2 { get; set; } + } - public class Source - { - public int Value { get; set; } - } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.RecognizeDestinationPrefixes("Foo", "Bar"); + cfg.CreateMap(); + }); - public interface IDestination - { - int Value { get; } - } + protected override void Because_of() + { + _destination = Mapper.Map(new Source { Value = 5, Value2 = 10 }); + } - public class Destination : IDestination - { - public Destination(int value) - { - Value = value; - } + [Fact] + public void Registered_prefixes_ignored() + { + _destination.FooValue.ShouldBe(5); + _destination.BarValue2.ShouldBe(10); + } +} - private Destination() - { - } +public class When_destination_type_has_private_members : AutoMapperSpecBase +{ + private IDestination _destination; - public int Value { get; private set; } - } + public class Source + { + public int Value { get; set; } + } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.CreateMap(); - }); + public interface IDestination + { + int Value { get; } + } - protected override void Because_of() + public class Destination : IDestination + { + public Destination(int value) { - _destination = Mapper.Map(new Source {Value = 5}); + Value = value; } - [Fact] - public void Should_use_private_accessors_and_constructors() + private Destination() { - _destination.Value.ShouldBe(5); } + + public int Value { get; private set; } } - public static class MapFromExtensions + protected override MapperConfiguration CreateConfiguration() => new(cfg => { - public static int Plus(this int left, int right) - { - return left + right; - } + cfg.CreateMap(); + }); + + protected override void Because_of() + { + _destination = Mapper.Map(new Source {Value = 5}); + } + + [Fact] + public void Should_use_private_accessors_and_constructors() + { + _destination.Value.ShouldBe(5); } } + +public static class MapFromExtensions +{ + public static int Plus(this int left, int right) + { + return left + right; + } +} \ No newline at end of file diff --git a/src/UnitTests/NullBehavior.cs b/src/UnitTests/NullBehavior.cs index 34d6b2ad2a..dcf4879285 100644 --- a/src/UnitTests/NullBehavior.cs +++ b/src/UnitTests/NullBehavior.cs @@ -8,6 +8,21 @@ namespace AutoMapper.UnitTests.NullBehavior { + public class NullCheckDefault : AutoMapperSpecBase + { + class Source + { + public string Value { get; } + } + class Destination + { + public int Length { get; set; } = 42; + } + protected override MapperConfiguration CreateConfiguration() => new(c => + c.CreateMap().ForMember(d => d.Length, o => o.MapFrom(s => s.Value.Length))); + [Fact] + public void Should_be_default() => Map(new Source()).Length.ShouldBe(0); + } public class When_mappping_null_with_DoNotAllowNull : AutoMapperSpecBase { class Source diff --git a/src/UnitTests/OpenGenerics.cs b/src/UnitTests/OpenGenerics.cs index f2345daf32..1e32e2c465 100644 --- a/src/UnitTests/OpenGenerics.cs +++ b/src/UnitTests/OpenGenerics.cs @@ -6,6 +6,32 @@ namespace AutoMapper.UnitTests { + public class SealGenerics : AutoMapperSpecBase + { + public record SourceProperty(T Value, SourceProperty Recursive = null); + public record DestProperty(T Value, DestProperty Recursive = null); + public record User(SourceProperty UserStoreId); + public class UserPropertiesContainer + { + public UserProperties User { get; set; } + } + public class UserProperties + { + public DestProperty UserStoreId { get; set; } + } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.CreateMap(typeof(SourceProperty<>), typeof(DestProperty<>)); + cfg.CreateMap().ForMember(dest => dest.User, opt => opt.MapFrom(src => src)); + cfg.CreateMap(); + }); + [Fact] + public void Should_work() + { + var guid = Guid.NewGuid(); + Map(new User(new(guid))).UserStoreId.Value.ShouldBe(guid); + } + } public class OpenGenerics_With_Struct : AutoMapperSpecBase { public struct Id diff --git a/src/UnitTests/Projection/ConstructorTests.cs b/src/UnitTests/Projection/ConstructorTests.cs index 063a8cedd5..aebfc9affe 100644 --- a/src/UnitTests/Projection/ConstructorTests.cs +++ b/src/UnitTests/Projection/ConstructorTests.cs @@ -80,6 +80,20 @@ class Destination [Fact] public void Should_construct_correctly() => new[] { new Source { Value = 5 } }.AsQueryable().ProjectTo(Configuration).First().Value.ShouldBe("5"); } + public class ConstructorMapFrom : AutoMapperSpecBase + { + class Source + { + public int Value { get; set; } + } + record Destination(bool Value) + { + } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + cfg.CreateProjection().ForCtorParam(nameof(Destination.Value), o=>o.MapFrom(s=>s.Value==5))); + [Fact] + public void Should_construct_correctly() => new[] { new Source { Value = 5 } }.AsQueryable().ProjectTo(Configuration).First().Value.ShouldBeTrue(); + } public class ConstructorIncludeMembers : AutoMapperSpecBase { class SourceWrapper diff --git a/src/UnitTests/ShouldMapMethod.cs b/src/UnitTests/ShouldMapMethod.cs index 683fe14015..dec8b1c161 100644 --- a/src/UnitTests/ShouldMapMethod.cs +++ b/src/UnitTests/ShouldMapMethod.cs @@ -2,140 +2,152 @@ using Shouldly; using System; -namespace AutoMapper.UnitTests +namespace AutoMapper.UnitTests; + +public class ShouldIgnoreOpenGenericMethods : NonValidatingSpecBase { - public class ShouldMapMethodInstanceMethods : NonValidatingSpecBase + protected override MapperConfiguration CreateConfiguration() => new(c => c.CreateMap()); + class Source { - public int SomeValue = 2354; - public int AnotherValue = 6798; - - private Destination _destination; - - class Source - { - private int _someValue; - private int _anotherValue; - - public Source(int someValue, int anotherValue) - { - _someValue = someValue; - anotherValue = _anotherValue; - } + public int GetValue() => 42; + } + class Destination + { + public int Value { get; set; } + } + [Fact] + public void Works() => Map(new Source()).Value.ShouldBe(0); +} +public class ShouldMapMethodInstanceMethods : NonValidatingSpecBase +{ + public int SomeValue = 2354; + public int AnotherValue = 6798; - public int SomeNumber() - { - return _someValue; - } + private Destination _destination; - public int AnotherNumber() { - return _anotherValue; - } - } + class Source + { + private int _someValue; + private int _anotherValue; - class Destination + public Source(int someValue, int anotherValue) { - public int SomeNumber { get; set; } - public int AnotherNumber { get; set; } + _someValue = someValue; + anotherValue = _anotherValue; } - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.ShouldMapMethod = (m => m.Name != nameof(Source.AnotherNumber)); - cfg.CreateMap(); - }); - - protected override void Because_of() + public int SomeNumber() { - _destination = Mapper.Map(new Source(SomeValue, AnotherValue)); + return _someValue; } - [Fact] - public void Should_report_unmapped_property() - { - new Action(AssertConfigurationIsValid) - .ShouldThrowException(ex => - { - ex.Errors.ShouldNotBeNull(); - ex.Errors.ShouldNotBeEmpty(); - ex.Errors[0].UnmappedPropertyNames.ShouldNotBeNull(); - ex.Errors[0].UnmappedPropertyNames.ShouldNotBeEmpty(); - ex.Errors[0].UnmappedPropertyNames[0].ShouldBe(nameof(Destination.AnotherNumber)); - }); + public int AnotherNumber() { + return _anotherValue; } + } - [Fact] - public void Should_not_map_another_number_method() - { - _destination.SomeNumber.ShouldBe(SomeValue); - _destination.AnotherNumber.ShouldNotBe(AnotherValue); - } + class Destination + { + public int SomeNumber { get; set; } + public int AnotherNumber { get; set; } } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.ShouldMapMethod = (m => m.Name != nameof(Source.AnotherNumber)); + cfg.CreateMap(); + }); - static class SourceExtensions + protected override void Because_of() { - public static int SomeNumber(this ShouldMapMethodExtensionMethods.Source source) - { - return source.SomeValue; - } + _destination = Mapper.Map(new Source(SomeValue, AnotherValue)); + } - public static int AnotherNumber(this ShouldMapMethodExtensionMethods.Source source) - { - return source.AnotherValue; - } + [Fact] + public void Should_report_unmapped_property() + { + new Action(AssertConfigurationIsValid) + .ShouldThrowException(ex => + { + ex.Errors.ShouldNotBeNull(); + ex.Errors.ShouldNotBeEmpty(); + ex.Errors[0].UnmappedPropertyNames.ShouldNotBeNull(); + ex.Errors[0].UnmappedPropertyNames.ShouldNotBeEmpty(); + ex.Errors[0].UnmappedPropertyNames[0].ShouldBe(nameof(Destination.AnotherNumber)); + }); } - public class ShouldMapMethodExtensionMethods : NonValidatingSpecBase + [Fact] + public void Should_not_map_another_number_method() { - public int SomeValue = 4698; - public int AnotherValue = 2374; + _destination.SomeNumber.ShouldBe(SomeValue); + _destination.AnotherNumber.ShouldNotBe(AnotherValue); + } +} - private Destination _destination; - public class Source - { - public int SomeValue { get; set; } - public int AnotherValue { get; set; } - } +static class SourceExtensions +{ + public static int SomeNumber(this ShouldMapMethodExtensionMethods.Source source) + { + return source.SomeValue; + } - public class Destination - { - public int SomeNumber { get; set; } - public int AnotherNumber { get; set; } - } + public static int AnotherNumber(this ShouldMapMethodExtensionMethods.Source source) + { + return source.AnotherValue; + } +} - protected override MapperConfiguration CreateConfiguration() => new(cfg => - { - cfg.IncludeSourceExtensionMethods(typeof(SourceExtensions)); - cfg.ShouldMapMethod = (m => m.Name != nameof(SourceExtensions.AnotherNumber)); - cfg.CreateMap(); - }); +public class ShouldMapMethodExtensionMethods : NonValidatingSpecBase +{ + public int SomeValue = 4698; + public int AnotherValue = 2374; - protected override void Because_of() - { - _destination = Mapper.Map(new Source { SomeValue = SomeValue, AnotherValue = AnotherValue }); - } + private Destination _destination; - [Fact] - public void Should_report_unmapped_property() - { - new Action(AssertConfigurationIsValid) - .ShouldThrowException(ex => - { - ex.Errors.ShouldNotBeNull(); - ex.Errors.ShouldNotBeEmpty(); - ex.Errors[0].UnmappedPropertyNames.ShouldNotBeNull(); - ex.Errors[0].UnmappedPropertyNames.ShouldNotBeEmpty(); - ex.Errors[0].UnmappedPropertyNames[0].ShouldBe(nameof(Destination.AnotherNumber)); - }); - } + public class Source + { + public int SomeValue { get; set; } + public int AnotherValue { get; set; } + } - [Fact] - public void Should_not_map_another_number_method() - { - _destination.SomeNumber.ShouldBe(SomeValue); - _destination.AnotherNumber.ShouldNotBe(AnotherValue); - } + public class Destination + { + public int SomeNumber { get; set; } + public int AnotherNumber { get; set; } } + protected override MapperConfiguration CreateConfiguration() => new(cfg => + { + cfg.IncludeSourceExtensionMethods(typeof(SourceExtensions)); + cfg.ShouldMapMethod = (m => m.Name != nameof(SourceExtensions.AnotherNumber)); + cfg.CreateMap(); + }); + + protected override void Because_of() + { + _destination = Mapper.Map(new Source { SomeValue = SomeValue, AnotherValue = AnotherValue }); + } + + [Fact] + public void Should_report_unmapped_property() + { + new Action(AssertConfigurationIsValid) + .ShouldThrowException(ex => + { + ex.Errors.ShouldNotBeNull(); + ex.Errors.ShouldNotBeEmpty(); + ex.Errors[0].UnmappedPropertyNames.ShouldNotBeNull(); + ex.Errors[0].UnmappedPropertyNames.ShouldNotBeEmpty(); + ex.Errors[0].UnmappedPropertyNames[0].ShouldBe(nameof(Destination.AnotherNumber)); + }); + } + + [Fact] + public void Should_not_map_another_number_method() + { + _destination.SomeNumber.ShouldBe(SomeValue); + _destination.AnotherNumber.ShouldNotBe(AnotherValue); + } } \ No newline at end of file