Reputation: 31
I want to use AutoMapper for mapping DTO s to Models. But my project contains some custom rules for default values for primitive types like [string null value is "_"], [default int value is -1] and [date time fields must be integer like 19990221] etc.
In my test i have a outer DTO which haves Null string values. But my inner system model has a "null string must be represents as a "_" string" rule (its sucks but its a legacy design decision).
Models:
public class OuterDTO
{
public string TestString { get; set; }
}
public class InnerModel
{
public string TestString { get; set; }
}
Automapper type converters:
public class CustomStringToStringConverter : ITypeConverter<string, string>
{
public string Convert(ResolutionContext context)
{
string source = (string)context.SourceValue;
if (source == "_")
return null;
return source;
}
}
public class StringToSafirStringConverter : ITypeConverter<string, string>
{
public string Convert(ResolutionContext context)
{
string source = (string)context.SourceValue;
if (source == null)
return "_";
return source;
}
}
And my Test is :
[TestMethod]
public void StringToSafirStringConverterAndSafirStringToStringConverter_SafirDefaultNullStringAndNullString_ReturnsNullAndSafirNullString()
{
//Arrenge
Mapper.CreateMap<string, string>().ConvertUsing(new StringToSafirStringConverter());//this has to used be DTO To Model map
Mapper.CreateMap<string, string>().ConvertUsing(new SafirStringToStringConverter());//this has to used be Model To DTO map
Mapper.CreateMap<InnerModel, OuterDTO>();
Mapper.CreateMap<OuterDTO, InnerModel>();
Mapper.AssertConfigurationIsValid();
InnerModel expected1 = new InnerModel()
{
TestString = null
};
OuterDTO inner1 = new OuterDTO()
{
TestString = "_"
};
OuterDTO expected2 = new OuterDTO()
{
TestString = "_"
};
InnerModel outer1 = new InnerModel()
{
TestString = null
};
//Act
InnerModel actual1 = Mapper.Map<OuterDTO, InnerModel>(inner1);
OuterDTO actual2 = Mapper.Map<InnerModel, OuterDTO>(outer1);
//Assert
Assert.AreEqual(expected1.TestString, actual1.TestString);
Assert.AreEqual(expected2.TestString, actual2.TestString);
}
Second assert fails.
I have to map DTO s to Models and Models to DTO s hundreds of cases and I have same issue in integer too. How can i choose converters to do different mappings?
Upvotes: 3
Views: 2251
Reputation: 73243
You're declaring two mappings from string to string:
Mapper.CreateMap<string, string>().ConvertUsing(new StringToSafirStringConverter());
Mapper.CreateMap<string, string>().ConvertUsing(new SafirStringToStringConverter());
Mappings cannot be overwritten, so if you look at the AutoMapper configuration you'll see just one mapping for string to string. (Even if you could define two mappings for string to string, AutoMapper could not know which one to use.)
The way to do this is to define the mapping for the property using ValueResolvers:
Mapper.CreateMap<InnerModel, OuterDTO>()
.ForMember(dest => dest.TestString,
opt => opt.ResolveUsing<StringToSafirStringResolver>()
.FromMember(source => source.TestString));
Mapper.CreateMap<OuterDTO, InnerModel>()
.ForMember(dest => dest.TestString,
opt => opt.ResolveUsing<SafirStringToStringResolver>()
.FromMember(source => source.TestString));
Then you write the ValueResolvers, say:
public class StringToSafirStringResolver : ValueResolver<string, string>
{
protected override string ResolveCore(string source)
{
return source ?? "_";
}
}
public class SafirStringToStringResolver : ValueResolver<string, string>
{
protected override string ResolveCore(string source)
{
return source == "_" ? null : source;
}
}
You can then do the same thing for your int and datetime values.
Upvotes: 2