Friday, 11 November 2011

More Advanced Use of AutoMapper - Part 2

Custom type converters
// Source and destination classes
public class Source
{
    public string Value1 { get; set; }
    public string Value2 { get; set; }
    public string Value3 { get; set; }
}
public class Destination
{
    public int Value1 { get; set; }
    public DateTime Value2 { get; set; }
    public Boolean Value3 { get; set; }
}


// Custom type converter classes
  //custom type converter class uses 'ITypeConverter' interface and has 'Convert' method
public class DateTimeTypeConverter : ITypeConverter<string, DateTime>
{
    public DateTime Convert(ResolutionContext context)
    {
        return System.Convert.ToDateTime(context.SourceValue);
    }
}
public class TypeTypeConverter : ITypeConverter<string, Boolean>
{
    public Boolean Convert(ResolutionContext context)
    {
        return System.Convert.ToBoolean(context.SourceValue);
    }
}


// Specify mappings
  //using .NET built in function
Mapper.CreateMap<string, int>().ConvertUsing(Convert.ToInt32);

  //using custom type converter classes
Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
Mapper.CreateMap<string, Boolean>().ConvertUsing<TypeTypeConverter>();

Mapper.CreateMap<Source, Destination>();


// Usage
var source = new Source
{
    Value1 = "5",
    Value2 = "01/01/2000",
    Value3 = "true"
};
Destination result = Mapper.Map<Source, Destination>(source);
With custom type converter, the mapping is applied automatically by AutoMapper when any source and destination types match. This mapping has a global scope.



Custom value resolvers
// Source and destination classes
public class SourceRsolvr
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}
public class DestinationRsolvr
{
    public int Total { get; set; }
}


// Custom resolver classes
  //custom resolver class derives from 'ValueResolver' and overrides 'ResolveCore' method, we can also create a
  //custom resolver class that derives from 'IValueResolver' but this is rarely used

  //a custom resolver without argument
public class CustomResolverOne : ValueResolver<SourceRsolvr, int>
{
    protected override int ResolveCore(SourceRsolvr source)
    {
        return source.Value1 + source.Value2;
    }
}

  //a custom resolver with argument
public class CustomResolverTwo : ValueResolver<SourceRsolvr, int>
{
    private readonly Expression<Func<int, bool>> _func;

    public CustomResolverTwo(Expression<Func<int, bool>> func)
    {
        _func = func;
    }

    protected override int ResolveCore(SourceRsolvr source)
    {
        var list = new[] { source.Value1, source.Value2};
        return list.Where(_func.Compile()).Sum();
    }
}


// Mapping
  // using custom resolver without argument
Mapper.CreateMap<SourceRsolvr, DestinationRsolvr>()
        .ForMember(dest => dest.Total, 
                   opt => opt.ResolveUsing<CustomResolverOne>()
                             .ConstructedBy(() => new CustomResolverOne()));

  // another example of using custom resolver with argument
//Mapper.CreateMap<SourceRsolvr, DestinationRsolvr>()
//        .ForMember(dest => dest.Total, 
//                   opt => opt.ResolveUsing<CustomResolverTwo>()
//                             .ConstructedBy(() => new CustomResolverTwo(x => x > 5)));


// Usage
var source = new SourceRsolvr
{
    Value1 = 5,
    Value2 = 7
};
var result = Mapper.Map<SourceRsolvr, DestinationRsolvr>(source);
Unlike custom type converter, custom value resolver needs to be specified in the configuration of any destination class' member that would like to apply it. Thus it will only be implemented to specific members that are configured for it.

Reference:
https://github.com/AutoMapper/AutoMapper/wiki

No comments: