Skip to content

Commit

Permalink
disallow mapping into a read-only collection without a setter
Browse files Browse the repository at this point in the history
not needed anymore

map read-only collections by default

cosmetic

When_mapping_to_a_getter_only_ienumerable

cosmetic

cosmetic

upgrade apicompat

cosmetic

cosmetic

add 3.1 target for the integration tests

update to Visual Studio 2019

retarget to 3.1

when mapping to an existing collection, we need ICollection, not IList
  • Loading branch information
lbargaoanu committed Apr 29, 2020
1 parent a5b3c34 commit bf2bf91
Show file tree
Hide file tree
Showing 39 changed files with 204 additions and 201 deletions.
2 changes: 1 addition & 1 deletion appveyor.yml
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/AutoMapper.csproj
Expand Up @@ -35,7 +35,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.DotNet.ApiCompat" Version="5.0.0-beta.20057.5" PrivateAssets="All" />
<PackageReference Include="Microsoft.DotNet.ApiCompat" Version="5.0.0-beta.20201.1" PrivateAssets="All" />
</ItemGroup>

<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
Expand Down
5 changes: 1 addition & 4 deletions src/AutoMapper/Configuration/Internal/PrimitiveHelper.cs
Expand Up @@ -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<,>));

Expand All @@ -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<>));
Expand Down
6 changes: 3 additions & 3 deletions src/AutoMapper/Configuration/MappingExpressionBase.cs
Expand Up @@ -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; }
Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -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}.");
}
Expand Down
Expand Up @@ -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);
}
Expand All @@ -335,7 +335,6 @@ private void Apply(PropertyMap propertyMap)
{
action(propertyMap);
}
propertyMap.CheckMappedReadonly();
}

public LambdaExpression SourceExpression => _sourceExpression;
Expand Down
3 changes: 0 additions & 3 deletions src/AutoMapper/Configuration/PrimitiveExtensions.cs
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions src/AutoMapper/DefaultMemberMap.cs
Expand Up @@ -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 { } }
Expand Down
8 changes: 4 additions & 4 deletions src/AutoMapper/Execution/DelegateFactory.cs
Expand Up @@ -25,7 +25,7 @@ private static Func<object> GenerateConstructor(Type type)
return Lambda<Func<object>>(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)
Expand All @@ -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);
}
Expand All @@ -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<,>))
Expand All @@ -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}.");
}
Expand Down
4 changes: 2 additions & 2 deletions src/AutoMapper/Execution/ProxyGenerator.cs
Expand Up @@ -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();
Expand Down Expand Up @@ -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));
}
Expand Down
17 changes: 9 additions & 8 deletions src/AutoMapper/Execution/TypeMapPlanBuilder.cs
Expand Up @@ -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
{
Expand Down Expand Up @@ -96,7 +97,7 @@ private void CheckForCycles(HashSet<TypeMap> typeMapsPath)
}
if(typeMapsPath.Contains(memberTypeMap))
{
if(memberTypeMap.SourceType.IsValueType())
if(memberTypeMap.SourceType.IsValueType)
{
if(memberTypeMap.MaxDepth == 0)
{
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand Down Expand Up @@ -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) }),
Expand Down Expand Up @@ -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)),
Expand Down
1 change: 1 addition & 0 deletions src/AutoMapper/IMemberMap.cs
Expand Up @@ -28,5 +28,6 @@ public interface IMemberMap
IEnumerable<ValueTransformerConfiguration> ValueTransformers { get; }
MemberInfo SourceMember { get; }
bool IsMapped { get; }
bool CanBeSet { get; }
}
}
4 changes: 2 additions & 2 deletions src/AutoMapper/Internal/ExpressionFactory.cs
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
6 changes: 3 additions & 3 deletions src/AutoMapper/Internal/ReflectionHelper.cs
Expand Up @@ -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);
}
Expand Down Expand Up @@ -83,7 +83,7 @@ public static IEnumerable<MemberInfo> 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];
}
Expand Down Expand Up @@ -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];
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/MapperConfiguration.cs
Expand Up @@ -465,7 +465,7 @@ private static Expression<UntypedMapperFunc> 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<UntypedMapperFunc>(
ToType(
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/Mappers/ArrayCopyMapper.cs
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/AutoMapper/Mappers/CollectionMapper.cs
Expand Up @@ -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)
Expand Down
9 changes: 4 additions & 5 deletions src/AutoMapper/Mappers/EnumerableMapper.cs
Expand Up @@ -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,
Expand Down
Expand Up @@ -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)))
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/Mappers/Internal/ElementTypeHelper.cs
Expand Up @@ -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<MethodInfo> GetStaticMethods(this Type type)
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/Mappers/ReadOnlyCollectionMapper.cs
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/Mappers/ReadOnlyDictionaryMapper.cs
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/PathMap.cs
Expand Up @@ -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; }
}
Expand Down

0 comments on commit bf2bf91

Please sign in to comment.