diff --git a/appveyor.yml b/appveyor.yml index 257f2fa2a1..64a2c50981 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ pull_requests: branches: only: - master -image: Visual Studio 2017 +image: Visual Studio 2019 nuget: disable_publish_on_pr: true build_script: diff --git a/src/AutoMapper/AutoMapper.csproj b/src/AutoMapper/AutoMapper.csproj index f16b9a7347..b587e36414 100644 --- a/src/AutoMapper/AutoMapper.csproj +++ b/src/AutoMapper/AutoMapper.csproj @@ -35,7 +35,7 @@ - + diff --git a/src/AutoMapper/Configuration/Internal/PrimitiveHelper.cs b/src/AutoMapper/Configuration/Internal/PrimitiveHelper.cs index 021c908763..a54d87e56e 100644 --- a/src/AutoMapper/Configuration/Internal/PrimitiveHelper.cs +++ b/src/AutoMapper/Configuration/Internal/PrimitiveHelper.cs @@ -43,9 +43,6 @@ public static bool IsQueryableType(Type type) public static bool IsListType(Type type) => typeof(IList).IsAssignableFrom(type); - public static bool IsListOrDictionaryType(Type type) - => type.IsListType() || type.IsDictionaryType(); - public static bool IsDictionaryType(Type type) => type.ImplementsGenericInterface(typeof(IDictionary<,>)); @@ -59,7 +56,7 @@ public static bool ImplementsGenericInterface(Type type, Type interfaceType) } public static bool IsGenericType(Type type, Type genericType) - => type.IsGenericType() && type.GetGenericTypeDefinition() == genericType; + => type.IsGenericType && type.GetGenericTypeDefinition() == genericType; public static Type GetIEnumerableType(Type type) => type.GetGenericInterface(typeof(IEnumerable<>)); diff --git a/src/AutoMapper/Configuration/MappingExpressionBase.cs b/src/AutoMapper/Configuration/MappingExpressionBase.cs index 77ccc066d3..ec0dfb9bd1 100644 --- a/src/AutoMapper/Configuration/MappingExpressionBase.cs +++ b/src/AutoMapper/Configuration/MappingExpressionBase.cs @@ -21,7 +21,7 @@ protected MappingExpressionBase(MemberList memberList, Type sourceType, Type des protected MappingExpressionBase(MemberList memberList, TypePair types) { Types = types; - IsOpenGeneric = types.SourceType.IsGenericTypeDefinition() || types.DestinationType.IsGenericTypeDefinition(); + IsOpenGeneric = types.SourceType.IsGenericTypeDefinition || types.DestinationType.IsGenericTypeDefinition; TypeMapActions.Add(tm => tm.ConfiguredMemberList = memberList); } public TypePair Types { get; } @@ -59,7 +59,7 @@ public void Configure(TypeMap typeMap) } var destTypeInfo = typeMap.DestinationTypeDetails; - if(!typeMap.DestinationType.IsAbstract()) + if(!typeMap.DestinationType.IsAbstract) { foreach(var destCtor in destTypeInfo.Constructors.OrderByDescending(ci => ci.GetParameters().Length)) { @@ -176,7 +176,7 @@ protected void IncludeCore(Type otherSourceType, Type otherDestinationType) protected void CheckIsDerived(Type derivedType, Type baseType) { - if(!baseType.IsAssignableFrom(derivedType) && !derivedType.IsGenericTypeDefinition() && !baseType.IsGenericTypeDefinition()) + if(!baseType.IsAssignableFrom(derivedType) && !derivedType.IsGenericTypeDefinition && !baseType.IsGenericTypeDefinition) { throw new ArgumentOutOfRangeException(nameof(derivedType), $"{derivedType} is not derived from {baseType}."); } diff --git a/src/AutoMapper/Configuration/MemberConfigurationExpression.cs b/src/AutoMapper/Configuration/MemberConfigurationExpression.cs index 3c19113b62..d1b5381bee 100644 --- a/src/AutoMapper/Configuration/MemberConfigurationExpression.cs +++ b/src/AutoMapper/Configuration/MemberConfigurationExpression.cs @@ -319,7 +319,7 @@ public void Configure(TypeMap typeMap) { var destMember = DestinationMember; - if(destMember.DeclaringType.IsGenericTypeDefinition()) + if(destMember.DeclaringType.IsGenericTypeDefinition) { destMember = typeMap.DestinationTypeDetails.PublicReadAccessors.Single(m => m.Name == destMember.Name); } @@ -335,7 +335,6 @@ private void Apply(PropertyMap propertyMap) { action(propertyMap); } - propertyMap.CheckMappedReadonly(); } public LambdaExpression SourceExpression => _sourceExpression; diff --git a/src/AutoMapper/Configuration/PrimitiveExtensions.cs b/src/AutoMapper/Configuration/PrimitiveExtensions.cs index 1cabbf8585..f345f1ba00 100644 --- a/src/AutoMapper/Configuration/PrimitiveExtensions.cs +++ b/src/AutoMapper/Configuration/PrimitiveExtensions.cs @@ -47,9 +47,6 @@ public static bool IsQueryableType(this Type type) public static bool IsListType(this Type type) => PrimitiveHelper.IsListType(type); - public static bool IsListOrDictionaryType(this Type type) - => PrimitiveHelper.IsListOrDictionaryType(type); - public static bool IsDictionaryType(this Type type) => PrimitiveHelper.IsDictionaryType(type); diff --git a/src/AutoMapper/DefaultMemberMap.cs b/src/AutoMapper/DefaultMemberMap.cs index fb0d1fbff1..9ebdaf9308 100644 --- a/src/AutoMapper/DefaultMemberMap.cs +++ b/src/AutoMapper/DefaultMemberMap.cs @@ -26,6 +26,7 @@ public class DefaultMemberMap : IMemberMap public virtual bool IsMapped => Ignored || CanResolveValue; public virtual bool Ignored { get => default; set { } } public virtual bool Inline { get => true; set { } } + public virtual bool CanBeSet => true; public virtual bool? UseDestinationValue { get => default; set { } } public virtual object NullSubstitute { get => default; set { } } public virtual LambdaExpression PreCondition { get => default; set { } } diff --git a/src/AutoMapper/Execution/DelegateFactory.cs b/src/AutoMapper/Execution/DelegateFactory.cs index 72c7681842..4bae9d6b1c 100644 --- a/src/AutoMapper/Execution/DelegateFactory.cs +++ b/src/AutoMapper/Execution/DelegateFactory.cs @@ -25,7 +25,7 @@ private static Func GenerateConstructor(Type type) return Lambda>(Convert(ctorExpr, typeof(object))).Compile(); } - public static Expression GenerateNonNullConstructorExpression(Type type) => type.IsValueType() + public static Expression GenerateNonNullConstructorExpression(Type type) => type.IsValueType ? Default(type) : (type == typeof(string) ? Constant(string.Empty) @@ -34,7 +34,7 @@ public static Expression GenerateNonNullConstructorExpression(Type type) => type public static Expression GenerateConstructorExpression(Type type) { - if (type.IsValueType()) + if (type.IsValueType) { return Default(type); } @@ -44,7 +44,7 @@ public static Expression GenerateConstructorExpression(Type type) return Constant(null, typeof(string)); } - if (type.IsInterface()) + if (type.IsInterface) { return type.IsDictionaryType() ? CreateCollection(type, typeof(Dictionary<,>)) @@ -54,7 +54,7 @@ public static Expression GenerateConstructorExpression(Type type) : InvalidType(type, $"Cannot create an instance of interface type {type}."); } - if (type.IsAbstract()) + if (type.IsAbstract) { return InvalidType(type, $"Cannot create an instance of abstract type {type}."); } diff --git a/src/AutoMapper/Execution/ProxyGenerator.cs b/src/AutoMapper/Execution/ProxyGenerator.cs index efa1d3113e..83315b6bea 100644 --- a/src/AutoMapper/Execution/ProxyGenerator.cs +++ b/src/AutoMapper/Execution/ProxyGenerator.cs @@ -55,7 +55,7 @@ private static Type EmitProxy(TypeDescription typeDescription) Debug.WriteLine(typeName, "Emitting proxy type"); TypeBuilder typeBuilder = proxyModule.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public, typeof(ProxyBase), - interfaceType.IsInterface() ? new[] { interfaceType } : Type.EmptyTypes); + interfaceType.IsInterface ? new[] { interfaceType } : Type.EmptyTypes); ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); ILGenerator ctorIl = constructorBuilder.GetILGenerator(); @@ -140,7 +140,7 @@ private static Type EmitProxy(TypeDescription typeDescription) public static Type GetProxyType(Type interfaceType) { var key = new TypeDescription(interfaceType); - if(!interfaceType.IsInterface()) + if(!interfaceType.IsInterface) { throw new ArgumentException("Only interfaces can be proxied", nameof(interfaceType)); } diff --git a/src/AutoMapper/Execution/TypeMapPlanBuilder.cs b/src/AutoMapper/Execution/TypeMapPlanBuilder.cs index ce743101a8..5322d61d6d 100644 --- a/src/AutoMapper/Execution/TypeMapPlanBuilder.cs +++ b/src/AutoMapper/Execution/TypeMapPlanBuilder.cs @@ -9,7 +9,8 @@ namespace AutoMapper.Execution using static System.Linq.Expressions.Expression; using static Internal.ExpressionFactory; using static ExpressionBuilder; - using System.Diagnostics; + using System.Diagnostics; + using Internal; public class TypeMapPlanBuilder { @@ -96,7 +97,7 @@ private void CheckForCycles(HashSet typeMapsPath) } if(typeMapsPath.Contains(memberTypeMap)) { - if(memberTypeMap.SourceType.IsValueType()) + if(memberTypeMap.SourceType.IsValueType) { if(memberTypeMap.MaxDepth == 0) { @@ -149,9 +150,9 @@ private LambdaExpression TypeConverterMapper() if (_typeMap.TypeConverterType == null) return null; Type type; - if (_typeMap.TypeConverterType.IsGenericTypeDefinition()) + if (_typeMap.TypeConverterType.IsGenericTypeDefinition) { - var genericTypeParam = _typeMap.SourceType.IsGenericType() + var genericTypeParam = _typeMap.SourceType.IsGenericType ? _typeMap.SourceType.GetTypeInfo().GenericTypeArguments[0] : _typeMap.DestinationTypeToUse.GetTypeInfo().GenericTypeArguments[0]; type = _typeMap.TypeConverterType.MakeGenericType(genericTypeParam); @@ -176,7 +177,7 @@ private Expression CreateDestinationFunc() { var newDestFunc = ToType(CreateNewDestinationFunc(), _typeMap.DestinationTypeToUse); - var getDest = _typeMap.DestinationTypeToUse.IsValueType() ? newDestFunc : Coalesce(_initialDestination, newDestFunc); + var getDest = _typeMap.DestinationTypeToUse.IsValueType ? newDestFunc : Coalesce(_initialDestination, newDestFunc); Expression destinationFunc = Assign(_destination, getDest); @@ -312,7 +313,7 @@ private Expression CreateNewDestinationFunc() { return CreateNewDestinationExpression(_typeMap.ConstructorMap); } - if(_typeMap.DestinationTypeToUse.IsInterface()) + if(_typeMap.DestinationTypeToUse.IsInterface) { var ctor = Call(null, typeof(DelegateFactory).GetDeclaredMethod(nameof(DelegateFactory.CreateCtor), new[] { typeof(Type) }), @@ -399,13 +400,13 @@ private Expression CreatePropertyMapFunc(IMemberMap memberMap, Expression destin getter = destMember; Expression destValueExpr; - if (memberMap.UseDestinationValue.GetValueOrDefault()) + if (memberMap.UseDestinationValue == true || (memberMap.UseDestinationValue == null && !ReflectionHelper.CanBeSet(destinationMember))) { destValueExpr = getter; } else { - if (_initialDestination.Type.IsValueType()) + if (_initialDestination.Type.IsValueType) destValueExpr = Default(memberMap.DestinationType); else destValueExpr = Condition(Equal(_initialDestination, Constant(null)), diff --git a/src/AutoMapper/IMemberMap.cs b/src/AutoMapper/IMemberMap.cs index dbb01e9d13..02ed667cda 100644 --- a/src/AutoMapper/IMemberMap.cs +++ b/src/AutoMapper/IMemberMap.cs @@ -28,5 +28,6 @@ public interface IMemberMap IEnumerable ValueTransformers { get; } MemberInfo SourceMember { get; } bool IsMapped { get; } + bool CanBeSet { get; } } } \ No newline at end of file diff --git a/src/AutoMapper/Internal/ExpressionFactory.cs b/src/AutoMapper/Internal/ExpressionFactory.cs index 1b94c1b1dd..39390fbb7d 100644 --- a/src/AutoMapper/Internal/ExpressionFactory.cs +++ b/src/AutoMapper/Internal/ExpressionFactory.cs @@ -165,7 +165,7 @@ public static Expression NullCheck(Expression expression, Type destinationType) while (true); void NullCheck() { - if (target == null || target.Type.IsValueType()) + if (target == null || target.Type.IsValueType) { return; } @@ -198,7 +198,7 @@ public static Expression Using(Expression disposable, Expression body) public static Expression IfNullElse(Expression expression, Expression then, Expression @else = null) { var nonNullElse = ToType(@else ?? Default(then.Type), then.Type); - if(expression.Type.IsValueType() && !expression.Type.IsNullableType()) + if(expression.Type.IsValueType && !expression.Type.IsNullableType()) { return nonNullElse; } diff --git a/src/AutoMapper/Internal/ReflectionHelper.cs b/src/AutoMapper/Internal/ReflectionHelper.cs index e4b33986a5..231dba1ee6 100644 --- a/src/AutoMapper/Internal/ReflectionHelper.cs +++ b/src/AutoMapper/Internal/ReflectionHelper.cs @@ -21,7 +21,7 @@ public static bool CanBeSet(MemberInfo propertyOrField) public static object GetDefaultValue(ParameterInfo parameter) { - if (parameter.DefaultValue == null && parameter.ParameterType.IsValueType()) + if (parameter.DefaultValue == null && parameter.ParameterType.IsValueType) { return Activator.CreateInstance(parameter.ParameterType); } @@ -83,7 +83,7 @@ public static IEnumerable GetMemberPath(Type type, string fullMember private static Type GetCurrentType(MemberInfo member, Type type) { var memberType = member?.GetMemberType() ?? type; - if (memberType.IsGenericType() && typeof(IEnumerable).IsAssignableFrom(memberType)) + if (memberType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(memberType)) { memberType = memberType.GetTypeInfo().GenericTypeArguments[0]; } @@ -169,7 +169,7 @@ public static Type ReplaceItemType(Type targetType, Type oldType, Type newType) if (targetType == oldType) return newType; - if (targetType.IsGenericType()) + if (targetType.IsGenericType) { var genSubArgs = targetType.GetTypeInfo().GenericTypeArguments; var newGenSubArgs = new Type[genSubArgs.Length]; diff --git a/src/AutoMapper/MapperConfiguration.cs b/src/AutoMapper/MapperConfiguration.cs index a1b408a25c..e13219b1be 100644 --- a/src/AutoMapper/MapperConfiguration.cs +++ b/src/AutoMapper/MapperConfiguration.cs @@ -465,7 +465,7 @@ private static Expression Wrap(MapRequest mapRequest, Delegat var requestedSourceType = mapRequest.RequestedTypes.SourceType; var requestedDestinationType = mapRequest.RequestedTypes.DestinationType; - var destination = requestedDestinationType.IsValueType() ? Coalesce(destinationParameter, New(requestedDestinationType)) : (Expression)destinationParameter; + var destination = requestedDestinationType.IsValueType ? Coalesce(destinationParameter, New(requestedDestinationType)) : (Expression)destinationParameter; // Invoking a delegate here return Lambda( ToType( diff --git a/src/AutoMapper/Mappers/ArrayCopyMapper.cs b/src/AutoMapper/Mappers/ArrayCopyMapper.cs index c383784b10..1eb57192ba 100644 --- a/src/AutoMapper/Mappers/ArrayCopyMapper.cs +++ b/src/AutoMapper/Mappers/ArrayCopyMapper.cs @@ -25,7 +25,7 @@ public class ArrayCopyMapper : ArrayMapper context.DestinationType.IsArray && context.SourceType.IsArray && ElementTypeHelper.GetElementType(context.DestinationType) == ElementTypeHelper.GetElementType(context.SourceType) - && ElementTypeHelper.GetElementType(context.SourceType).IsPrimitive(); + && ElementTypeHelper.GetElementType(context.SourceType).IsPrimitive; public override Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, IMemberMap memberMap, Expression sourceExpression, Expression destExpression, Expression contextExpression) diff --git a/src/AutoMapper/Mappers/CollectionMapper.cs b/src/AutoMapper/Mappers/CollectionMapper.cs index 327e939ec5..83d5e8c517 100644 --- a/src/AutoMapper/Mappers/CollectionMapper.cs +++ b/src/AutoMapper/Mappers/CollectionMapper.cs @@ -9,7 +9,8 @@ namespace AutoMapper.Mappers public class CollectionMapper : EnumerableMapperBase { - public override bool IsMatch(TypePair context) => context.SourceType.IsEnumerableType() && context.DestinationType.IsCollectionType(); + public override bool IsMatch(TypePair context) => context.SourceType.IsEnumerableType() && + (context.DestinationType.IsCollectionType() || context.DestinationType.IsListType()); public override Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, IMemberMap memberMap, Expression sourceExpression, Expression destExpression, Expression contextExpression) diff --git a/src/AutoMapper/Mappers/EnumerableMapper.cs b/src/AutoMapper/Mappers/EnumerableMapper.cs index c9f724e771..b714cadd45 100644 --- a/src/AutoMapper/Mappers/EnumerableMapper.cs +++ b/src/AutoMapper/Mappers/EnumerableMapper.cs @@ -12,16 +12,15 @@ namespace AutoMapper.Mappers public class EnumerableMapper : EnumerableMapperBase { - public override bool IsMatch(TypePair context) => (context.DestinationType.IsInterface() && context.DestinationType.IsEnumerableType() || - context.DestinationType.IsListType()) - && context.SourceType.IsEnumerableType(); + public override bool IsMatch(TypePair context) => + context.SourceType.IsEnumerableType() && (context.DestinationType.IsInterface && context.DestinationType.IsEnumerableType()); public override Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, IMemberMap memberMap, Expression sourceExpression, Expression destExpression, Expression contextExpression) { - if(destExpression.Type.IsInterface()) + if(destExpression.Type.IsInterface) { - var listType = typeof(IList<>).MakeGenericType(ElementTypeHelper.GetElementType(destExpression.Type)); + var listType = typeof(ICollection<>).MakeGenericType(ElementTypeHelper.GetElementType(destExpression.Type)); destExpression = Convert(destExpression, listType); } return MapCollectionExpression(configurationProvider, profileMap, memberMap, sourceExpression, diff --git a/src/AutoMapper/Mappers/Internal/CollectionMapperExpressionFactory.cs b/src/AutoMapper/Mappers/Internal/CollectionMapperExpressionFactory.cs index a75e477d9c..19d1c0c279 100644 --- a/src/AutoMapper/Mappers/Internal/CollectionMapperExpressionFactory.cs +++ b/src/AutoMapper/Mappers/Internal/CollectionMapperExpressionFactory.cs @@ -70,17 +70,21 @@ void UseDestinationValue() else { destination = newExpression; - Expression createInstance = passedDestination.Type.NewExpr(ifInterfaceType); - var isReadOnly = Property(ToType(passedDestination, destinationCollectionType), "IsReadOnly"); - assignNewExpression = Assign(newExpression, - Condition(OrElse(Equal(passedDestination, Constant(null)), isReadOnly), ToType(createInstance, passedDestination.Type), passedDestination)); + var createInstance = passedDestination.Type.NewExpr(ifInterfaceType); + var shouldCreateDestination = Equal(passedDestination, Constant(null)); + if (memberMap?.CanBeSet == true) + { + var isReadOnly = Property(ToType(passedDestination, destinationCollectionType), "IsReadOnly"); + shouldCreateDestination = OrElse(shouldCreateDestination, isReadOnly); + } + assignNewExpression = Assign(newExpression, Condition(shouldCreateDestination, ToType(createInstance, passedDestination.Type), passedDestination)); } } } private static Expression NewExpr(this Type baseType, Type ifInterfaceType) { - var newExpr = baseType.IsInterface() + var newExpr = baseType.IsInterface ? New( ifInterfaceType.MakeGenericType(GetElementTypes(baseType, ElementTypeFlags.BreakKeyValuePair))) diff --git a/src/AutoMapper/Mappers/Internal/ElementTypeHelper.cs b/src/AutoMapper/Mappers/Internal/ElementTypeHelper.cs index 1568242927..b19afd7890 100644 --- a/src/AutoMapper/Mappers/Internal/ElementTypeHelper.cs +++ b/src/AutoMapper/Mappers/Internal/ElementTypeHelper.cs @@ -55,7 +55,7 @@ public static class ElementTypeHelper public static Type GetEnumerationType(Type enumType) { - return !enumType.IsEnum() ? null : enumType; + return !enumType.IsEnum ? null : enumType; } internal static IEnumerable GetStaticMethods(this Type type) diff --git a/src/AutoMapper/Mappers/ReadOnlyCollectionMapper.cs b/src/AutoMapper/Mappers/ReadOnlyCollectionMapper.cs index 0cea444981..089050b96e 100644 --- a/src/AutoMapper/Mappers/ReadOnlyCollectionMapper.cs +++ b/src/AutoMapper/Mappers/ReadOnlyCollectionMapper.cs @@ -14,7 +14,7 @@ public class ReadOnlyCollectionMapper : IObjectMapper { public bool IsMatch(TypePair context) { - if (!(context.SourceType.IsEnumerableType() && context.DestinationType.IsGenericType())) + if (!(context.SourceType.IsEnumerableType() && context.DestinationType.IsGenericType)) return false; var genericType = context.DestinationType.GetGenericTypeDefinition(); diff --git a/src/AutoMapper/Mappers/ReadOnlyDictionaryMapper.cs b/src/AutoMapper/Mappers/ReadOnlyDictionaryMapper.cs index ac0bb92458..de9ee6a595 100644 --- a/src/AutoMapper/Mappers/ReadOnlyDictionaryMapper.cs +++ b/src/AutoMapper/Mappers/ReadOnlyDictionaryMapper.cs @@ -16,7 +16,7 @@ public class ReadOnlyDictionaryMapper : IObjectMapper { public bool IsMatch(TypePair context) { - if (!(context.SourceType.IsEnumerableType() && context.DestinationType.IsGenericType())) + if (!(context.SourceType.IsEnumerableType() && context.DestinationType.IsGenericType)) return false; var genericType = context.DestinationType.GetGenericTypeDefinition(); diff --git a/src/AutoMapper/PathMap.cs b/src/AutoMapper/PathMap.cs index f7aeb91318..ec836164e1 100644 --- a/src/AutoMapper/PathMap.cs +++ b/src/AutoMapper/PathMap.cs @@ -38,7 +38,7 @@ public PathMap(LambdaExpression destinationExpression, MemberPath memberPath, Ty public override string DestinationName => MemberPath.ToString(); public override bool CanResolveValue => !Ignored; - + public override bool CanBeSet => ReflectionHelper.CanBeSet(MemberPath.Last); public override bool Ignored { get; set; } public override LambdaExpression Condition { get; set; } } diff --git a/src/AutoMapper/Profile.cs b/src/AutoMapper/Profile.cs index d52987b76c..723ce5a19f 100644 --- a/src/AutoMapper/Profile.cs +++ b/src/AutoMapper/Profile.cs @@ -110,7 +110,7 @@ public IMappingExpression CreateMap(Type sourceType, Type destinationType, Membe _typeMapConfigs.Add(map); - if (sourceType.IsGenericTypeDefinition() || destinationType.IsGenericTypeDefinition()) + if (sourceType.IsGenericTypeDefinition || destinationType.IsGenericTypeDefinition) _openTypeMapConfigs.Add(map); return map; diff --git a/src/AutoMapper/ProfileMap.cs b/src/AutoMapper/ProfileMap.cs index ef60cacc78..516dac7be3 100644 --- a/src/AutoMapper/ProfileMap.cs +++ b/src/AutoMapper/ProfileMap.cs @@ -170,14 +170,14 @@ public TypeMap CreateClosedGenericTypeMap(ITypeMapConfiguration openMapConfig, T if(closedMap.TypeConverterType != null) { var typeParams = - (openMapConfig.SourceType.IsGenericTypeDefinition() ? closedTypes.SourceType.GetGenericArguments() : Type.EmptyTypes) + (openMapConfig.SourceType.IsGenericTypeDefinition ? closedTypes.SourceType.GetGenericArguments() : Type.EmptyTypes) .Concat - (openMapConfig.DestinationType.IsGenericTypeDefinition() ? closedTypes.DestinationType.GetGenericArguments() : Type.EmptyTypes); + (openMapConfig.DestinationType.IsGenericTypeDefinition ? closedTypes.DestinationType.GetGenericArguments() : Type.EmptyTypes); var neededParameters = closedMap.TypeConverterType.GetGenericParameters().Length; closedMap.TypeConverterType = closedMap.TypeConverterType.MakeGenericType(typeParams.Take(neededParameters).ToArray()); } - if(closedMap.DestinationTypeOverride?.IsGenericTypeDefinition() == true) + if(closedMap.DestinationTypeOverride?.IsGenericTypeDefinition == true) { var neededParameters = closedMap.DestinationTypeOverride.GetGenericParameters().Length; closedMap.DestinationTypeOverride = closedMap.DestinationTypeOverride.MakeGenericType(closedTypes.DestinationType.GetGenericArguments().Take(neededParameters).ToArray()); @@ -190,8 +190,8 @@ public ITypeMapConfiguration GetGenericMap(TypePair closedTypes) return _openTypeMapConfigs .SelectMany(tm => tm.ReverseTypeMap == null ? new[] { tm } : new[] { tm, tm.ReverseTypeMap }) .Where(tm => - tm.Types.SourceType.GetGenericTypeDefinitionIfGeneric() == closedTypes.SourceType.GetGenericTypeDefinitionIfGeneric() && - tm.Types.DestinationType.GetGenericTypeDefinitionIfGeneric() == closedTypes.DestinationType.GetGenericTypeDefinitionIfGeneric()) + tm.Types.SourceType.GetTypeDefinitionIfGeneric() == closedTypes.SourceType.GetTypeDefinitionIfGeneric() && + tm.Types.DestinationType.GetTypeDefinitionIfGeneric() == closedTypes.DestinationType.GetTypeDefinitionIfGeneric()) .OrderByDescending(tm => tm.DestinationType == closedTypes.DestinationType) // Favor more specific destination matches, .ThenByDescending(tm => tm.SourceType == closedTypes.SourceType) // then more specific source matches .FirstOrDefault(); diff --git a/src/AutoMapper/PropertyMap.cs b/src/AutoMapper/PropertyMap.cs index dbecd0d73b..3c9daa96e9 100644 --- a/src/AutoMapper/PropertyMap.cs +++ b/src/AutoMapper/PropertyMap.cs @@ -55,6 +55,7 @@ private void ApplyIncludedMemberMap(PropertyMap includedMemberMap, LambdaExpress public override IReadOnlyCollection SourceMembers => _memberChain; public override LambdaExpression CustomSource { get; set; } public override bool Inline { get; set; } = true; + public override bool CanBeSet => ReflectionHelper.CanBeSet(DestinationMember); public override bool Ignored { get; set; } public bool AllowNull { get; set; } public int? MappingOrder { get; set; } @@ -112,7 +113,7 @@ public void MapFrom(LambdaExpression sourceMember) public void MapFrom(string propertyOrField) { - var mapExpression = TypeMap.SourceType.IsGenericTypeDefinition() ? + var mapExpression = TypeMap.SourceType.IsGenericTypeDefinition ? // just a placeholder so the member is mapped Lambda(Constant(null)) : MemberAccessLambda(TypeMap.SourceType, propertyOrField); @@ -121,13 +122,5 @@ public void MapFrom(string propertyOrField) public void AddValueTransformation(ValueTransformerConfiguration valueTransformerConfiguration) => _valueTransformerConfigs.Add(valueTransformerConfiguration); - - internal void CheckMappedReadonly() - { - if(IsResolveConfigured && !ReflectionHelper.CanBeSet(DestinationMember)) - { - UseDestinationValue = true; - } - } } } \ No newline at end of file diff --git a/src/AutoMapper/QueryableExtensions/Impl/NullableSourceExpressionBinder.cs b/src/AutoMapper/QueryableExtensions/Impl/NullableSourceExpressionBinder.cs index 2e55de35e3..c4f9d27777 100644 --- a/src/AutoMapper/QueryableExtensions/Impl/NullableSourceExpressionBinder.cs +++ b/src/AutoMapper/QueryableExtensions/Impl/NullableSourceExpressionBinder.cs @@ -16,6 +16,6 @@ public MemberAssignment Build(IConfigurationProvider configuration, PropertyMap } public bool IsMatch(PropertyMap propertyMap, TypeMap propertyTypeMap, ExpressionResolutionResult result) => - result.Type.IsNullableType() && !propertyMap.DestinationType.IsNullableType() && propertyMap.DestinationType.IsValueType(); + result.Type.IsNullableType() && !propertyMap.DestinationType.IsNullableType() && propertyMap.DestinationType.IsValueType; } } \ No newline at end of file diff --git a/src/AutoMapper/QueryableExtensions/Impl/QueryMapperVisitor.cs b/src/AutoMapper/QueryableExtensions/Impl/QueryMapperVisitor.cs index e5b5d22e8e..f46ade8c71 100644 --- a/src/AutoMapper/QueryableExtensions/Impl/QueryMapperVisitor.cs +++ b/src/AutoMapper/QueryableExtensions/Impl/QueryMapperVisitor.cs @@ -120,7 +120,7 @@ private Expression VisitOrderBy(MethodCallExpression node) // for typical orderby expression, a unaryexpression is used that contains a // func which in turn defines the type of the field that has to be used for ordering/sorting - if (newOrderByExpr is UnaryExpression unary && unary.Operand.Type.IsGenericType()) + if (newOrderByExpr is UnaryExpression unary && unary.Operand.Type.IsGenericType) { methodArgs[1] = methodArgs[1].ReplaceItemType(typeof(string), unary.Operand.Type.GetGenericArguments().Last()); } @@ -148,7 +148,7 @@ private MethodInfo ChangeMethodArgTypeFormSourceToDest(MethodInfo mi) private Type ChangeLambdaArgTypeFormSourceToDest(Type lambdaType, Type returnType) { - if (lambdaType.IsGenericType()) + if (lambdaType.IsGenericType) { var genArgs = lambdaType.GetTypeInfo().GenericTypeArguments; var newGenArgs = genArgs.Select(t => t.ReplaceItemType(_sourceType, _destinationType)).ToArray(); diff --git a/src/AutoMapper/TypeDetails.cs b/src/AutoMapper/TypeDetails.cs index 6019b8b0c5..f24f01c3cd 100644 --- a/src/AutoMapper/TypeDetails.cs +++ b/src/AutoMapper/TypeDetails.cs @@ -52,12 +52,10 @@ private IEnumerable PossibleNames(string memberName, IEnumerable yield return s; } - private static IEnumerable PostFixes(IEnumerable postfixes, string name) - { - return - postfixes.Where(postfix => name.EndsWith(postfix, StringComparison.OrdinalIgnoreCase)) - .Select(postfix => name.Remove(name.Length - postfix.Length)); - } + private static IEnumerable PostFixes(IEnumerable postfixes, string name) => + postfixes + .Where(postfix => name.EndsWith(postfix, StringComparison.OrdinalIgnoreCase)) + .Select(postfix => name.Remove(name.Length - postfix.Length)); private static Func MembersToMap( Func shouldMapProperty, @@ -103,9 +101,9 @@ private IEnumerable BuildPublicNoArgExtensionMethods(IEnumerable method.GetParameters()[0].ParameterType == Type); - var genericInterfaces = Type.GetTypeInfo().ImplementedInterfaces.Where(t => t.IsGenericType()); + var genericInterfaces = Type.GetTypeInfo().ImplementedInterfaces.Where(t => t.IsGenericType); - if (Type.IsInterface() && Type.IsGenericType()) + if (Type.IsInterface && Type.IsGenericType) { genericInterfaces = genericInterfaces.Union(new[] { Type }); } @@ -113,7 +111,7 @@ private IEnumerable BuildPublicNoArgExtensionMethods(IEnumerable allMembers) - { + private static MemberInfo[] BuildPublicReadAccessors(IEnumerable allMembers) => // Multiple types may define the same property (e.g. the class and multiple interfaces) - filter this to one of those properties - var filteredMembers = allMembers + allMembers .OfType() .GroupBy(x => x.Name) // group properties of the same name together .Select(x => x.First()) - .Concat(allMembers.Where(x => x is FieldInfo)); // add FieldInfo objects back - - return filteredMembers.ToArray(); - } + .Concat(allMembers.Where(x => x is FieldInfo)) // add FieldInfo objects back + .ToArray(); - private static MemberInfo[] BuildPublicAccessors(IEnumerable allMembers) - { + private static MemberInfo[] BuildPublicAccessors(IEnumerable allMembers) => // Multiple types may define the same property (e.g. the class and multiple interfaces) - filter this to one of those properties - var filteredMembers = allMembers + allMembers .OfType() .GroupBy(x => x.Name) // group properties of the same name together - .Select(x => - x.Any(y => y.CanWrite && y.CanRead) - ? // favor the first property that can both read & write - otherwise pick the first one - x.First(y => y.CanWrite && y.CanRead) - : x.First()) - .Where(pi => pi.CanWrite || pi.PropertyType.IsListOrDictionaryType()) - //.OfType() // cast back to MemberInfo so we can add back FieldInfo objects - .Concat(allMembers.Where(x => x is FieldInfo)); // add FieldInfo objects back - - return filteredMembers.ToArray(); - } + .Select(x => x.FirstOrDefault(y => y.CanWrite && y.CanRead) ?? x.First()) // favor the first property that can both read & write - otherwise pick the first one + .Concat(allMembers.Where(x => x is FieldInfo)) // add FieldInfo objects back + .ToArray(); private IEnumerable GetAllPublicReadableMembers(Func membersToMap) => GetAllPublicMembers(PropertyReadable, FieldReadable, membersToMap); @@ -167,9 +153,7 @@ private IEnumerable GetAllPublicWritableMembers(Func GetAllPublicMembers(PropertyWritable, FieldWritable, membersToMap); private IEnumerable GetAllConstructors(Func shouldUseConstructor) - { - return Type.GetDeclaredConstructors().Where(shouldUseConstructor).ToArray(); - } + => Type.GetDeclaredConstructors().Where(shouldUseConstructor).ToArray(); private static bool PropertyReadable(PropertyInfo propertyInfo) => propertyInfo.CanRead; @@ -185,10 +169,10 @@ private IEnumerable GetAllConstructors(Func memberAvailableFor) { var typesToScan = new List(); - for (var t = Type; t != null; t = t.BaseType()) + for (var t = Type; t != null; t = t.BaseType) typesToScan.Add(t); - if (Type.IsInterface()) + if (Type.IsInterface) typesToScan.AddRange(Type.GetTypeInfo().ImplementedInterfaces); // Scan all types for public properties and fields @@ -214,4 +198,4 @@ private MethodInfo[] BuildPublicNoArgMethods(Func shouldMapMet .ToArray(); } } -} +} \ No newline at end of file diff --git a/src/AutoMapper/TypeExtensions.cs b/src/AutoMapper/TypeExtensions.cs index d7caeb1180..464ebff699 100644 --- a/src/AutoMapper/TypeExtensions.cs +++ b/src/AutoMapper/TypeExtensions.cs @@ -6,19 +6,16 @@ namespace AutoMapper { - internal static class TypeExtensions { public static bool Has(this MemberInfo member) where TAttribute : Attribute => member.GetCustomAttribute() != null; - public static Type GetGenericTypeDefinitionIfGeneric(this Type type) => type.IsGenericType() ? type.GetGenericTypeDefinition() : type; + public static Type GetTypeDefinitionIfGeneric(this Type type) => type.IsGenericType ? type.GetGenericTypeDefinition() : type; - public static Type[] GetGenericArguments(this Type type) => type.GetTypeInfo().GenericTypeArguments; + public static IEnumerable GetDeclaredConstructors(this Type type) => type.GetTypeInfo().DeclaredConstructors; public static Type[] GetGenericParameters(this Type type) => type.GetGenericTypeDefinition().GetTypeInfo().GenericTypeParameters; - public static IEnumerable GetDeclaredConstructors(this Type type) => type.GetTypeInfo().DeclaredConstructors; - public static Type CreateType(this TypeBuilder type) => type.CreateTypeInfo().AsType(); public static IEnumerable GetDeclaredMembers(this Type type) => type.GetTypeInfo().DeclaredMembers; @@ -27,11 +24,11 @@ public static IEnumerable GetTypeInheritance(this Type type) { yield return type; - var baseType = type.BaseType(); + var baseType = type.BaseType; while(baseType != null) { yield return baseType; - baseType = baseType.BaseType(); + baseType = baseType.BaseType; } } @@ -50,14 +47,9 @@ public static IEnumerable GetTypeInheritance(this Type type) public static IEnumerable GetAllMethods(this Type type) => type.GetRuntimeMethods(); - public static IEnumerable GetDeclaredProperties(this Type type) => type.GetTypeInfo().DeclaredProperties; - public static PropertyInfo GetDeclaredProperty(this Type type, string name) => type.GetTypeInfo().GetDeclaredProperty(name); - public static object[] GetCustomAttributes(this Type type, Type attributeType, bool inherit) - => type.GetTypeInfo().GetCustomAttributes(attributeType, inherit).Cast().ToArray(); - public static bool IsStatic(this FieldInfo fieldInfo) => fieldInfo?.IsStatic ?? false; public static bool IsStatic(this PropertyInfo propertyInfo) => propertyInfo?.GetGetMethod(true)?.IsStatic @@ -85,49 +77,6 @@ public static bool HasAnInaccessibleSetter(this PropertyInfo property) public static bool IsPublic(this MemberInfo memberInfo) => (memberInfo as FieldInfo)?.IsPublic ?? (memberInfo as PropertyInfo).IsPublic(); - public static bool IsNotPublic(this ConstructorInfo constructorInfo) => constructorInfo.IsPrivate - || constructorInfo.IsFamilyAndAssembly - || constructorInfo.IsFamilyOrAssembly - || constructorInfo.IsFamily; - public static Assembly Assembly(this Type type) => type.GetTypeInfo().Assembly; - - public static Type BaseType(this Type type) => type.GetTypeInfo().BaseType; - - public static bool IsAssignableFrom(this Type type, Type other) => type.GetTypeInfo().IsAssignableFrom(other.GetTypeInfo()); - - public static bool IsAbstract(this Type type) => type.GetTypeInfo().IsAbstract; - - public static bool IsClass(this Type type) => type.GetTypeInfo().IsClass; - - public static bool IsEnum(this Type type) => type.GetTypeInfo().IsEnum; - - public static bool IsGenericType(this Type type) => type.GetTypeInfo().IsGenericType; - - public static bool IsGenericTypeDefinition(this Type type) => type.GetTypeInfo().IsGenericTypeDefinition; - - public static bool IsInterface(this Type type) => type.GetTypeInfo().IsInterface; - - public static bool IsPrimitive(this Type type) => type.GetTypeInfo().IsPrimitive; - - public static bool IsSealed(this Type type) => type.GetTypeInfo().IsSealed; - - public static bool IsValueType(this Type type) => type.GetTypeInfo().IsValueType; - - public static bool IsLiteralType(this Type type) => type == typeof(string) || type.GetTypeInfo().IsValueType; - - public static bool IsInstanceOfType(this Type type, object o) => o != null && type.GetTypeInfo().IsAssignableFrom(o.GetType().GetTypeInfo()); - - public static PropertyInfo[] GetProperties(this Type type) => type.GetRuntimeProperties().ToArray(); - - public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo, bool ignored) => propertyInfo.GetMethod; - - public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo, bool ignored) => propertyInfo.SetMethod; - - public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo) => propertyInfo.GetMethod; - - public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo) => propertyInfo.SetMethod; - - public static FieldInfo GetField(this Type type, string name) => type.GetRuntimeField(name); } -} +} \ No newline at end of file diff --git a/src/AutoMapper/TypeMap.cs b/src/AutoMapper/TypeMap.cs index beef92d2f7..9a6c30f186 100644 --- a/src/AutoMapper/TypeMap.cs +++ b/src/AutoMapper/TypeMap.cs @@ -116,10 +116,10 @@ public PathMap FindOrCreatePathMapFor(LambdaExpression destinationExpression, Me || CustomCtorFunction != null || ConstructDestinationUsingServiceLocator || ConstructorMap?.CanResolve == true - || DestinationTypeToUse.IsInterface() - || DestinationTypeToUse.IsAbstract() - || DestinationTypeToUse.IsGenericTypeDefinition() - || DestinationTypeToUse.IsValueType() + || DestinationTypeToUse.IsInterface + || DestinationTypeToUse.IsAbstract + || DestinationTypeToUse.IsGenericTypeDefinition + || DestinationTypeToUse.IsValueType || DestinationTypeDetails.Constructors.FirstOrDefault(c => c.GetParameters().All(p => p.IsOptional)) != null; public bool IsConstructorMapping => diff --git a/src/AutoMapper/TypePair.cs b/src/AutoMapper/TypePair.cs index 8199e01aba..94a837d1f6 100644 --- a/src/AutoMapper/TypePair.cs +++ b/src/AutoMapper/TypePair.cs @@ -98,9 +98,8 @@ public static TypePair Create(TSource source, Type sourceType, Type des { return null; } - var sourceGenericDefinition = SourceType.IsGenericType() ? SourceType.GetGenericTypeDefinition() : SourceType; - var destinationGenericDefinition = DestinationType.IsGenericType() ? DestinationType.GetGenericTypeDefinition() : DestinationType; - + var sourceGenericDefinition = SourceType.GetTypeDefinitionIfGeneric(); + var destinationGenericDefinition = DestinationType.GetTypeDefinitionIfGeneric(); return new TypePair(sourceGenericDefinition, destinationGenericDefinition); } @@ -116,8 +115,8 @@ public TypePair CloseGenericTypes(TypePair closedTypes) { destinationArguments = sourceArguments; } - var closedSourceType = SourceType.IsGenericTypeDefinition() ? SourceType.MakeGenericType(sourceArguments) : SourceType; - var closedDestinationType = DestinationType.IsGenericTypeDefinition() ? DestinationType.MakeGenericType(destinationArguments) : DestinationType; + var closedSourceType = SourceType.IsGenericTypeDefinition ? SourceType.MakeGenericType(sourceArguments) : SourceType; + var closedDestinationType = DestinationType.IsGenericTypeDefinition ? DestinationType.MakeGenericType(destinationArguments) : DestinationType; return new TypePair(closedSourceType, closedDestinationType); } diff --git a/src/IntegrationTests/AutoMapper.IntegrationTests.csproj b/src/IntegrationTests/AutoMapper.IntegrationTests.csproj index 4849858bf5..67446ea78b 100644 --- a/src/IntegrationTests/AutoMapper.IntegrationTests.csproj +++ b/src/IntegrationTests/AutoMapper.IntegrationTests.csproj @@ -1,7 +1,7 @@  - net461 + net461;netcoreapp3.1 AutoMapper.IntegrationTests AutoMapper.IntegrationTests true @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/UnitTests/ArraysAndLists.cs b/src/UnitTests/ArraysAndLists.cs index 7f66d335fc..ebc61fdeaa 100644 --- a/src/UnitTests/ArraysAndLists.cs +++ b/src/UnitTests/ArraysAndLists.cs @@ -274,6 +274,38 @@ public class Book { public string Name { get; set; } } + } + + public class When_mapping_to_an_existing_HashSet_typed_as_IEnumerable : AutoMapperSpecBase + { + private Destination _destination = new Destination(); + + public class Source + { + public int[] IntCollection { get; set; } = new int[0]; + } + + public class Destination + { + public IEnumerable IntCollection { get; set; } = new HashSet { 1, 2, 3, 4, 5 }; + public string Unmapped { get; } + } + + protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + }); + + protected override void Because_of() + { + _destination = Mapper.Map(new Source(), _destination); + } + + [Fact] + public void Should_clear_the_destination() + { + _destination.IntCollection.Count().ShouldBe(0); + } } public class When_mapping_to_an_existing_array_typed_as_IEnumerable : AutoMapperSpecBase @@ -399,7 +431,59 @@ public void Should_map_from_the_generic_list_of_values_with_formatting() _destination.Values2.ShouldContain("7"); _destination.Values2.ShouldContain("6"); } - } + } + + public class When_mapping_to_a_getter_only_ienumerable : AutoMapperSpecBase + { + private Destination _destination = new Destination(); + public class Source + { + public int[] Values { get; set; } + public List Values2 { get; set; } + } + public class Destination + { + public IEnumerable Values { get; } = new List(); + public IEnumerable Values2 { get; } = new List(); + } + protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + }); + protected override void Because_of() => _destination = Mapper.Map(new Source { Values = new[] { 1, 2, 3, 4 }, Values2 = new List { 9, 8, 7, 6 } }); + [Fact] + public void Should_map_the_list_of_source_items() + { + _destination.Values.ShouldBe(new[] { 1, 2, 3, 4 }); + _destination.Values2.ShouldBe(new[] { "9", "8", "7", "6" }); + } + } + + public class When_mapping_to_a_getter_only_existing_ienumerable : AutoMapperSpecBase + { + private Destination _destination = new Destination(); + public class Source + { + public int[] Values { get; set; } + public List Values2 { get; set; } + } + public class Destination + { + public IEnumerable Values { get; } = new List(); + public IEnumerable Values2 { get; } = new List(); + } + protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg => + { + cfg.CreateMap(); + }); + protected override void Because_of() => Mapper.Map(new Source { Values = new[] { 1, 2, 3, 4 }, Values2 = new List { 9, 8, 7, 6 } }, _destination); + [Fact] + public void Should_map_the_list_of_source_items() + { + _destination.Values.ShouldBe(new[] { 1, 2, 3, 4 }); + _destination.Values2.ShouldBe(new[]{ "9", "8", "7", "6" }); + } + } public class When_mapping_to_a_concrete_non_generic_icollection : AutoMapperSpecBase { diff --git a/src/UnitTests/AutoMapper.UnitTests.csproj b/src/UnitTests/AutoMapper.UnitTests.csproj index 2e7fe215ff..24a2d4cf98 100644 --- a/src/UnitTests/AutoMapper.UnitTests.csproj +++ b/src/UnitTests/AutoMapper.UnitTests.csproj @@ -1,7 +1,7 @@  - net461;netcoreapp2.0; + net461;netcoreapp3.1; AutoMapper.UnitTests AutoMapper.UnitTests $(NoWarn);649;618 diff --git a/src/UnitTests/Bug/MapAtRuntime/MapAtRuntime.cs b/src/UnitTests/Bug/MapAtRuntime/MapAtRuntime.cs index 416897a150..4a657d0355 100644 --- a/src/UnitTests/Bug/MapAtRuntime/MapAtRuntime.cs +++ b/src/UnitTests/Bug/MapAtRuntime/MapAtRuntime.cs @@ -36,7 +36,7 @@ public class MapAtRuntime : AutoMapperSpecBase cfg.CreateMap().ReverseMap(); cfg.CreateMap().ReverseMap(); cfg.CreateMap().ReverseMap(); - //cfg.ForAllPropertyMaps(p => !p.SourceType.IsValueType(), (pm, o) => o.MapAtRuntime()); + //cfg.ForAllPropertyMaps(p => !p.SourceType.IsValueType, (pm, o) => o.MapAtRuntime()); }); public class Initialize diff --git a/src/UnitTests/Bug/MapAtRuntimeWithCollections/MapAtRuntimeWithCollections.cs b/src/UnitTests/Bug/MapAtRuntimeWithCollections/MapAtRuntimeWithCollections.cs index 55049c3b2f..8a32774af1 100644 --- a/src/UnitTests/Bug/MapAtRuntimeWithCollections/MapAtRuntimeWithCollections.cs +++ b/src/UnitTests/Bug/MapAtRuntimeWithCollections/MapAtRuntimeWithCollections.cs @@ -37,7 +37,7 @@ public class MapAtRuntimeWithCollections : AutoMapperSpecBase cfg.CreateMap().ReverseMap(); cfg.CreateMap().ReverseMap(); cfg.CreateMap().ReverseMap(); - //cfg.ForAllPropertyMaps(p => !p.SourceType.IsValueType(), (pm, o) => o.MapAtRuntime()); + //cfg.ForAllPropertyMaps(p => !p.SourceType.IsValueType, (pm, o) => o.MapAtRuntime()); }); public class Initialize diff --git a/src/UnitTests/CollectionMapping.cs b/src/UnitTests/CollectionMapping.cs index 5103a9cd92..c5016fc921 100644 --- a/src/UnitTests/CollectionMapping.cs +++ b/src/UnitTests/CollectionMapping.cs @@ -396,6 +396,24 @@ public void Should_map_ok() } } + public class When_mapping_to_readonly_collection_without_setter : AutoMapperSpecBase + { + public class Source + { + public IEnumerable MyCollection { get; } = new[] { "one", "two" }; + } + public class Destination + { + public IEnumerable MyCollection { get; } = new ReadOnlyCollection(new string[0]); + } + protected override MapperConfiguration Configuration => new MapperConfiguration(cfg => cfg.CreateMap()); + [Fact] + public void Should_fail() => new Action(() => Mapper.Map(new Source(), new Destination())) + .ShouldThrow() + .InnerException.ShouldBeOfType() + .Message.ShouldBe("Collection is read-only."); + } + public class When_mapping_to_readonly_property_UseDestinationValue : AutoMapperSpecBase { public class Source diff --git a/src/UnitTests/Internal/PrimitiveExtensionsTester.cs b/src/UnitTests/Internal/PrimitiveExtensionsTester.cs index 21eabbe09c..fd4c1b1e4a 100644 --- a/src/UnitTests/Internal/PrimitiveExtensionsTester.cs +++ b/src/UnitTests/Internal/PrimitiveExtensionsTester.cs @@ -29,29 +29,5 @@ public void Should_find_explicitly_implemented_member() { PrimitiveHelper.GetFieldOrProperty(typeof(DestinationClass), "Value").ShouldNotBeNull(); } - - [Fact] - public void Should_not_flag_only_enumerable_type_as_writeable_collection() - { - PrimitiveHelper.IsListOrDictionaryType(typeof(string)).ShouldBeFalse(); - } - - [Fact] - public void Should_flag_list_as_writable_collection() - { - PrimitiveHelper.IsListOrDictionaryType(typeof(int[])).ShouldBeTrue(); - } - - [Fact] - public void Should_flag_generic_list_as_writeable_collection() - { - PrimitiveHelper.IsListOrDictionaryType(typeof(List)).ShouldBeTrue(); - } - - [Fact] - public void Should_flag_dictionary_as_writeable_collection() - { - PrimitiveHelper.IsListOrDictionaryType(typeof(Dictionary)).ShouldBeTrue(); - } } } \ No newline at end of file diff --git a/src/UnitTests/Mappers/CustomMapperTests.cs b/src/UnitTests/Mappers/CustomMapperTests.cs index 3ed859ff93..313c441cbb 100644 --- a/src/UnitTests/Mappers/CustomMapperTests.cs +++ b/src/UnitTests/Mappers/CustomMapperTests.cs @@ -150,7 +150,7 @@ class EnumMapper : ObjectMapper public override bool IsMatch(TypePair types) { var underlyingType = Nullable.GetUnderlyingType(types.SourceType) ?? types.SourceType; - return underlyingType.IsEnum() && types.DestinationType == typeof(string); + return underlyingType.IsEnum && types.DestinationType == typeof(string); } public override string Map(object source, string destination, Type sourceType, Type destinationType, ResolutionContext context)