Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/AutoMapper/Configuration/IProfileConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ public interface IProfileConfiguration
/// </summary>
Func<FieldInfo, bool> ShouldMapField { get; }

/// <summary>
/// Specify which methods, of those that are eligible (public, parameterless, and non-static or extension methods), should be mapped.
/// By default all eligible methods are mapped.
/// </summary>
Func<MethodInfo, bool> ShouldMapMethod { get; }


/// <summary>
/// Specify which constructors should be considered for the destination objects.
/// By default all constructors are considered.
Expand Down
1 change: 1 addition & 0 deletions src/AutoMapper/IProfileExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ public interface IProfileExpression

Func<PropertyInfo, bool> ShouldMapProperty { get; set; }
Func<FieldInfo, bool> ShouldMapField { get; set; }
Func<MethodInfo, bool> ShouldMapMethod { get; set; }
Func<ConstructorInfo, bool> ShouldUseConstructor { get; set; }

string ProfileName { get; }
Expand Down
1 change: 1 addition & 0 deletions src/AutoMapper/Profile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ IEnumerable<Action<PropertyMap, IMemberConfigurationExpression>> IProfileConfigu
public bool? EnableNullPropagationForQueryMapping { get; set; }
public Func<PropertyInfo, bool> ShouldMapProperty { get; set; }
public Func<FieldInfo, bool> ShouldMapField { get; set; }
public Func<MethodInfo, bool> ShouldMapMethod { get; set; }
public Func<ConstructorInfo, bool> ShouldUseConstructor { get; set; }

public INamingConvention SourceMemberNamingConvention { get; set; }
Expand Down
6 changes: 4 additions & 2 deletions src/AutoMapper/ProfileMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration)
EnableNullPropagationForQueryMapping = profile.EnableNullPropagationForQueryMapping ?? configuration?.EnableNullPropagationForQueryMapping ?? false;
ConstructorMappingEnabled = profile.ConstructorMappingEnabled ?? configuration?.ConstructorMappingEnabled ?? true;
ShouldMapField = profile.ShouldMapField ?? configuration?.ShouldMapField ?? (p => p.IsPublic());
ShouldMapProperty = profile.ShouldMapProperty ?? configuration?.ShouldMapProperty ?? (p => p.IsPublic());
ShouldMapProperty = profile.ShouldMapProperty ?? configuration?.ShouldMapProperty ?? (p => p.IsPublic());
ShouldMapMethod = profile.ShouldMapMethod ?? configuration?.ShouldMapMethod ?? (p => true);
ShouldUseConstructor = profile.ShouldUseConstructor ?? configuration?.ShouldUseConstructor ?? (c => true);
CreateMissingTypeMaps = profile.CreateMissingTypeMaps ?? configuration?.CreateMissingTypeMaps ?? true;
ValidateInlineMaps = profile.ValidateInlineMaps ?? configuration?.ValidateInlineMaps ?? true;
Expand Down Expand Up @@ -83,7 +84,8 @@ public ProfileMap(IProfileConfiguration profile, IConfiguration configuration)
public bool EnableNullPropagationForQueryMapping { get; }
public string Name { get; }
public Func<FieldInfo, bool> ShouldMapField { get; }
public Func<PropertyInfo, bool> ShouldMapProperty { get; }
public Func<PropertyInfo, bool> ShouldMapProperty { get; }
public Func<MethodInfo, bool> ShouldMapMethod { get; }
public Func<ConstructorInfo, bool> ShouldUseConstructor { get; }

public IEnumerable<Action<PropertyMap, IMemberConfigurationExpression>> AllPropertyMapActions { get; }
Expand Down
7 changes: 4 additions & 3 deletions src/AutoMapper/TypeDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public TypeDetails(Type type, ProfileMap config)
var publicWritableMembers = GetAllPublicWritableMembers(membersToMap);
PublicReadAccessors = BuildPublicReadAccessors(publicReadableMembers);
PublicWriteAccessors = BuildPublicAccessors(publicWritableMembers);
PublicNoArgMethods = BuildPublicNoArgMethods();
PublicNoArgMethods = BuildPublicNoArgMethods(config.ShouldMapMethod);
Constructors = GetAllConstructors(config.ShouldUseConstructor);
PublicNoArgExtensionMethods = BuildPublicNoArgExtensionMethods(config.SourceExtensionMethods);
PublicNoArgExtensionMethods = BuildPublicNoArgExtensionMethods(config.SourceExtensionMethods.Where(config.ShouldMapMethod));
AllMembers = PublicReadAccessors.Concat(PublicNoArgMethods).Concat(PublicNoArgExtensionMethods).ToList();
DestinationMemberNames = AllMembers.Select(mi => new DestinationMemberName { Member = mi, Possibles = PossibleNames(mi.Name, config.Prefixes, config.Postfixes).ToArray() });
}
Expand Down Expand Up @@ -211,9 +211,10 @@ private IEnumerable<MemberInfo> GetAllPublicMembers(
);
}

private MethodInfo[] BuildPublicNoArgMethods()
private MethodInfo[] BuildPublicNoArgMethods(Func<MethodInfo, bool> shouldMapMethod)
{
return Type.GetAllMethods()
.Where(shouldMapMethod)
.Where(mi => mi.IsPublic && !mi.IsStatic && mi.DeclaringType != typeof(object))
.Where(m => (m.ReturnType != typeof(void)) && (m.GetParameters().Length == 0))
.ToArray();
Expand Down
141 changes: 141 additions & 0 deletions src/UnitTests/ShouldMapMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using Xunit;
using Shouldly;
using System;

namespace AutoMapper.UnitTests
{
public class ShouldMapMethodInstanceMethods : NonValidatingSpecBase
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, but the extension method case needs a test too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I'm gonna add it.

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 SomeNumber()
{
return _someValue;
}

public int AnotherNumber() {
return _anotherValue;
}
}

class Destination
{
public int SomeNumber { get; set; }
public int AnotherNumber { get; set; }
}

protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg =>
{
cfg.ShouldMapMethod = (m => m.Name != nameof(Source.AnotherNumber));
cfg.CreateMap<Source, Destination>();
});

protected override void Because_of()
{
_destination = Mapper.Map<Source, Destination>(new Source(SomeValue, AnotherValue));
}

[Fact]
public void Should_report_unmapped_property()
{
new Action(() => Configuration.AssertConfigurationIsValid())
.ShouldThrowException<AutoMapperConfigurationException>(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);
}
}


static class SourceExtensions
{
public static int SomeNumber(this ShouldMapMethodExtensionMethods.Source source)
{
return source.SomeValue;
}

public static int AnotherNumber(this ShouldMapMethodExtensionMethods.Source source)
{
return source.AnotherValue;
}
}

public class ShouldMapMethodExtensionMethods : NonValidatingSpecBase
{
public int SomeValue = 4698;
public int AnotherValue = 2374;

private Destination _destination;

public class Source
{
public int SomeValue { get; set; }
public int AnotherValue { get; set; }
}

public class Destination
{
public int SomeNumber { get; set; }
public int AnotherNumber { get; set; }
}

protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg =>
{
cfg.IncludeSourceExtensionMethods(typeof(SourceExtensions));
cfg.ShouldMapMethod = (m => m.Name != nameof(SourceExtensions.AnotherNumber));
cfg.CreateMap<Source, Destination>();
});

protected override void Because_of()
{
_destination = Mapper.Map<Source, Destination>(new Source { SomeValue = SomeValue, AnotherValue = AnotherValue });
}

[Fact]
public void Should_report_unmapped_property()
{
new Action(() => Configuration.AssertConfigurationIsValid())
.ShouldThrowException<AutoMapperConfigurationException>(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);
}
}

}