Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate closed generic maps #2849

Merged
merged 1 commit into from Nov 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/AutoMapper/ConfigurationValidator.cs
Expand Up @@ -57,10 +57,19 @@ private void DryRunTypeMap(ICollection<TypeMap> typeMapsChecked, TypePair types,
{
if(typeMap == null)
{
if (types.SourceType.IsGenericParameter || types.DestinationType.IsGenericParameter)
{
return;
}
typeMap = _config.ResolveTypeMap(types.SourceType, types.DestinationType);
}
if (typeMap != null)
{
if (typeMap.IsClosedGeneric)
{
// it was already validated
return;
}
// dynamic maps get mapped at runtime yolo
if (typeMap.IsConventionMap && typeMap.Profile.CreateMissingTypeMaps)
{
Expand Down
4 changes: 2 additions & 2 deletions src/AutoMapper/Mapper.cs
Expand Up @@ -186,8 +186,8 @@ public Mapper(IConfigurationProvider configurationProvider)

public Mapper(IConfigurationProvider configurationProvider, Func<Type, object> serviceCtor)
{
_configurationProvider = configurationProvider;
_serviceCtor = serviceCtor;
_configurationProvider = configurationProvider ?? throw new ArgumentNullException(nameof(configurationProvider));
_serviceCtor = serviceCtor ?? throw new ArgumentNullException(nameof(serviceCtor));
DefaultContext = new ResolutionContext(new ObjectMappingOperationOptions(serviceCtor), this);
}

Expand Down
6 changes: 5 additions & 1 deletion src/AutoMapper/MapperConfiguration.cs
Expand Up @@ -218,6 +218,10 @@ public TypeMap ResolveTypeMap(TypePair typePair, ITypeMapConfiguration inlineCon
{
inlineConfiguration.Configure(typeMap);
typeMap.Seal(this);
if (typeMap.IsClosedGeneric)
{
AssertConfigurationIsValid(typeMap);
}
}
}
return typeMap;
Expand Down Expand Up @@ -297,7 +301,7 @@ public void AssertConfigurationIsValid()
{
_expressionValidator.AssertConfigurationExpressionIsValid();

_validator.AssertConfigurationIsValid(_typeMapRegistry.Values.Where(tm => !tm.SourceType.IsGenericTypeDefinition() && !tm.DestinationType.IsGenericTypeDefinition()));
_validator.AssertConfigurationIsValid(_typeMapRegistry.Values);
}

public IMapper CreateMapper() => new Mapper(this);
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/ProfileMap.cs
Expand Up @@ -198,7 +198,7 @@ public TypeMap CreateInlineMap(TypePair types, IConfigurationProvider configurat
public TypeMap CreateClosedGenericTypeMap(ITypeMapConfiguration openMapConfig, TypePair closedTypes, IConfigurationProvider configurationProvider)
{
var closedMap = _typeMapFactory.CreateTypeMap(closedTypes.SourceType, closedTypes.DestinationType, this);

closedMap.IsClosedGeneric = true;
openMapConfig.Configure(closedMap);

Configure(closedMap, configurationProvider);
Expand Down
13 changes: 7 additions & 6 deletions src/AutoMapper/PropertyMap.cs
Expand Up @@ -7,7 +7,8 @@

namespace AutoMapper
{
using static Internal.ExpressionFactory;
using static Internal.ExpressionFactory;
using static Expression;

[DebuggerDisplay("{DestinationMember.Name}")]
public class PropertyMap : IMemberMap
Expand Down Expand Up @@ -116,11 +117,11 @@ public void MapFrom(LambdaExpression sourceMember)

public void MapFrom(string propertyOrField)
{
if(TypeMap.SourceType.IsGenericTypeDefinition())
{
return;
}
MapFrom(MemberAccessLambda(TypeMap.SourceType, propertyOrField));
var mapExpression = TypeMap.SourceType.IsGenericTypeDefinition() ?
// just a placeholder so the member is mapped
Lambda(Default(DestinationType)) :
MemberAccessLambda(TypeMap.SourceType, propertyOrField);
MapFrom(mapExpression);
}

public void AddValueTransformation(ValueTransformerConfiguration valueTransformerConfiguration)
Expand Down
2 changes: 2 additions & 0 deletions src/AutoMapper/TypeMap.cs
Expand Up @@ -128,6 +128,8 @@ public PathMap FindOrCreatePathMapFor(LambdaExpression destinationExpression, Me
&& ConfiguredMemberList != MemberList.None
&& !(IsValid ?? false);

public bool IsClosedGeneric { get; internal set; }

public bool ConstructorParameterMatches(string destinationPropertyName) =>
ConstructorMap?.CtorParams.Any(c => !c.HasDefaultValue && string.Equals(c.Parameter.Name, destinationPropertyName, StringComparison.OrdinalIgnoreCase)) == true;

Expand Down
4 changes: 2 additions & 2 deletions src/UnitTests/Bug/UseDestinationValue.cs
Expand Up @@ -85,7 +85,7 @@ public CollectionController(object owner)
{
cfg.CreateMap<OrganizationDTO, Organization>().ForMember(d=>d.BranchCollection, o=>o.UseDestinationValue());
cfg.CreateMap<BranchDTO, Branch>();
cfg.CreateMap(typeof(CollectionDTOController<,>), typeof(CollectionController<,,>));
cfg.CreateMap(typeof(CollectionDTOController<,>), typeof(CollectionController<,,>), MemberList.None);
});

protected override void Because_of()
Expand Down Expand Up @@ -178,7 +178,7 @@ public CollectionController(object owner)
{
cfg.CreateMap<OrganizationDTO, Organization>();
cfg.CreateMap<BranchDTO, Branch>();
cfg.CreateMap(typeof(CollectionDTOController<,>), typeof(CollectionController<,,>));
cfg.CreateMap(typeof(CollectionDTOController<,>), typeof(CollectionController<,,>), MemberList.None);
});

[Fact]
Expand Down
3 changes: 2 additions & 1 deletion src/UnitTests/InterfaceMapping.cs
Expand Up @@ -101,7 +101,8 @@ public interface IDestinationBase<T>

protected override MapperConfiguration Configuration => new MapperConfiguration(cfg=>
cfg.CreateMap(typeof(IList<>), typeof(IDestinationBase<>))
.ForMember(nameof(IDestinationBase<Object>.Items), p_Expression => p_Expression.MapFrom(p_Source => p_Source)));
.ForMember(nameof(IDestinationBase<Object>.Items), p_Expression => p_Expression.MapFrom(p_Source => p_Source))
.ForMember("PropertyToMap", o=>o.Ignore()));

[Fact]
public void Should_work()
Expand Down
81 changes: 81 additions & 0 deletions src/UnitTests/OpenGenerics.cs
Expand Up @@ -2,6 +2,8 @@
{
using MappingInheritance;
using Shouldly;
using System;
using System.Linq;
using Xunit;

public class RecursiveOpenGenerics : AutoMapperSpecBase
Expand Down Expand Up @@ -42,6 +44,85 @@ public void Should_work()
}
}

public class OpenGenericsValidation : NonValidatingSpecBase
{
public class Source<T>
{
public T Value { get; set; }
}

public class Dest<T>
{
public int A { get; set; }
public T Value { get; set; }
}

protected override MapperConfiguration Configuration => new MapperConfiguration(cfg => cfg.CreateMap(typeof(Source<>), typeof(Dest<>)));

[Fact]
public void Should_report_unmapped_property()
{
new Action(()=>Mapper.Map<Dest<int>>(new Source<int>{ Value = 5 }))
.ShouldThrow<AutoMapperConfigurationException>()
.Errors.Single().UnmappedPropertyNames.Single().ShouldBe("A");
}
}

public class OpenGenericsProfileValidationNonGenericMembers : NonValidatingSpecBase
{
public class Source<T>
{
public T[] Value { get; set; }
}

public class Dest<T>
{
public int A { get; set; }
public T[] Value { get; set; }
}

class MyProfile : Profile
{
public MyProfile()
{
CreateMap(typeof(Source<>), typeof(Dest<>));
}
}

protected override MapperConfiguration Configuration => new MapperConfiguration(cfg => cfg.AddProfile<MyProfile>());

[Fact]
public void Should_report_unmapped_property()
{
new Action(()=>Configuration.AssertConfigurationIsValid<MyProfile>())
.ShouldThrow<AutoMapperConfigurationException>()
.Errors.Single().UnmappedPropertyNames.Single().ShouldBe("A"); ;
}
}

public class OpenGenericsProfileValidation : AutoMapperSpecBase
{
public class Source<T>
{
public T[] Value { get; set; }
}

public class Dest<T>
{
public T[] Value { get; set; }
}

class MyProfile : Profile
{
public MyProfile()
{
CreateMap(typeof(Source<>), typeof(Dest<>));
}
}

protected override MapperConfiguration Configuration => new MapperConfiguration(cfg => cfg.AddProfile<MyProfile>());
}

public class OpenGenerics
{
public class Source<T>
Expand Down