Resolvers¶
Resolvers are used to solve the mapping between two members based on their types.
The Providers determines a sequence that the resolvers are executed and each one is only triggered if the previous one was not able solve the mapping.
You don’t need to worry about resolvers, this documentation is just to explain how each one works.
Bellow, we are listing all built-in Output’s resolvers and what they can do.
TypesAssignableResolver¶
Can map the members with the same type or when it’s underlying type matches.
| Input Type | Output Type | Is able to map? |
| int | int | |
| int | int? | |
| int? | int | |
| byte | int | |
| decimal | string | |
| etc. |
PrimitiveResolver¶
When the type is primitive or is decimal type, this resolver tries to map by conversion.
| Input Type | Output Type | Is able to map? |
| byte | int | |
| long | decimal | |
| int? | int |
GuidResolver¶
Can convert a System.Guid to string or to a byte array and vice-versa.
| Input Type | Output Type | Is able to map? |
| Enum | string or byte[] | |
| string or byte[] | Enum |
Mapping from a string or byte array to a Guid, requires of course, a valid data format otherwise an Exception will occur.
EnumResolver¶
Can convert an Enum to all it’s approved types ( byte, sbyte, short, ushort, int, uint, long or ulong), to a string or to an equivalent Enum.
The reverse map also works.
| Input Type | Output Type | Is able to map? |
| Approved Types / String | Enum | |
| Enum | Approved Types / String | |
| Enum | Enum (Equivalent) |
e.g:
public enum Level
{
None = 0,
Gold = 1,
Silver = 2,
Bronze = 3
}
public class Source
{
public Level LevelA { get; set; }
public Level LevelB { get; set; }
public Level LevelC { get; set; }
}
public enum Another
{
None = 0,
Gold = 1,
Silver = 2,
Bronze = 3
}
public class Target
{
public Another LevelA { get; set; }
public string LevelB { get; set; }
public int LevelC { get; set; }
}
// Mapping to the Target a source like:
new Source
{
LevelA = Level.Gold,
LevelB = Level.Silver,
LevelC = Level.Bronze
});
// Will result in:
-> Target {
LevelA == Another.Gold
LevelB == "LevelB"
LevelC == 3
}
DictionaryResolver¶
It can map a Dictionary<TKeyType, TValueType> to another Dictionary<TAnotherKeyType, TAnotherValueType>.
The types of Key and Value are resolved by the Resolvers described on this page.
In the example bellow, the dictionary Key is solved by TypesAssignableResolver and the dictionary Value by EnumResolver.
| Input Type | Output Type | Is able to map? |
| Dictionary<int, MyEnum> | Dictionary<int, string> | |
| etc. | ||
CollectionResolver¶
This resolver can convert many types derived from generics ICollection<T> or IReadOnlyCollection<T> to another type derived from the same.
There are many possibilities here, so, to simplify I will show just a few possibilities.
| Input Type | Output Type | Is able to map? |
| List<T> | T[] | |
| T[] | HashSet<T> | |
| Stack<T> | Queue<T> | |
| etc. | ||
ClassResolver¶
This resolver is used when the input and output members represents a class.
Internally, it just uses the current mapper to do a new mapping.
AnyToStringResolver¶
Converts any type to string calling the implementation of .ToString from the TInput
The source type
.
ConstructorResolver¶
The constructor resolver is a special kind of resolver used to determine how the destination object must be instantiated.
It prioritizes the parameterless constructor but when the TOutput The destination type object doesn’t have one, it tries to determine one based on TInput The source type properties.
e.g:
public class Sample
{
public string Title { get; set; }
public DateTime RegistrationDate { get; set; }
public bool IsActive { get; set; }
}
public class SampleDto
{
public SampleDto(string title, bool isActive)
{
Title = title;
IsActive = isActive;
}
public string Title { get; set; }
public DateTime RegistrationDate { get; set; }
public bool IsActive { get; set; }
}
As we can see, SampleDto has only one constructor with title and isActive as parameters.
Doing a mapping from Sample to SampleDto, the ConstructorResolver will try find the public Properties “Title” and “IsActive” in our Sample instance to populate the constructor parameters.
It operates in case insensitive, so does not matter if we have “isActive”, “ISACTIVE” or “isactive”. All these are considered as long they are public Properties.
An AmbiguosMatchException can occur of course, if we have two public properties with the same name declared with different text cases.
In case of multiple constructors, the execution order is from the one with highest parameters amount to the one with lowest.
| Constructor | Execution order |
| SampleDto(string title, DateTime RegistrationDate, bool IsActive) | 1 |
| SampleDto(string, bool IsActive) | 2 |
| SampleDto(string title) | 3 |
| etc. |
Remember, the priority is the parameterless constructor, only if it doesn’t exists that the others are going to be used.
Complex Scenarios¶
Prefixed properties¶
public class FooDto
{
public string Name { get; set; }
public int CreatedDateYear { get; set; }
public int CreatedDateMonth { get; set; }
public int CreatedDateDay { get; set; }
public string BarName { get; set; }
}
public class Foo
{
public Foo(string name, DateTime createdDate, Bar bar)
{
Name = name;
CreatedDate = createdDate;
Bar = bar;
}
public string Name { get; }
public DateTime CreatedDate { get; }
public Bar Bar { get; }
}
public class Bar
{
public Bar(string name)
{
Name = name;
}
public string Name { get; }
}
Mapping FooDto to Foo also works.
As FooDto doesn’t have a CreatedDate property of System.Date type, the ConstructorResolver look for all properties prefixed by CreatedDate.
In this case, CreatedDateYear, CreatedDateMonth and CreatedDateDay.
With Year, Month and Day the ConstructorResolver is able to build the CreatedDate of Foo using the constructor DateTime(year, month, day).
The same method is used to solve the Bar constructor.
Custom map properties¶
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class CustomerDto
{
public CustomerDto(string fullName)
{
FullName = fullName;
}
public string FullName { get; set; }
}
To map the property CustomerDto.FullName with the values of Customer.FirstName and Customer.LastName we need write a custom configuration. See more in Mapping Configurations.
var provider = new MappingProvider();
provider.AddConfig<Customer, CustomerDto>(config =>
config.Map(
output => output.FullName,
input => input.FirstName + " " + input.LastName
)
);
var mapper = new Mapper(provider);
mapper.Map<CustomerDto>(customer);
Doing this, the ConstructorResolver will also use this configuration to determine how to resolve the fullName parameter of the constructor.