Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does AutoMapper have an IValueFormatter when it has a seemingly much more powerful ValueResolver?

It looks like an IValueFormatter takes a value of type object and returns a value of type string, while a ValueResolver<TSource, TDestination> takes a value of any type and returns a value of any type. So, it's more flexible. There is also the matter that, with a ValueResolver, you never need to cast the source to a particular type--you define it explicitly in your class definition.

Given this, why use IValueFormatter? Does it do anything that can't be done with ValueResolver? Am I misunderstanding how it works?

like image 663
devuxer Avatar asked Jan 31 '10 03:01

devuxer


2 Answers

The big difference is that formatters can be applied at the member, profile, type and global level. So you can do something like "ForSourceType.AddFormatter() in a profile, and now blammo! All your decimals now show up as money. Resolvers are strictly for custom member mapping.

like image 52
Jimmy Bogard Avatar answered Nov 10 '22 07:11

Jimmy Bogard


Okay, I think I've figured this out:

  • With an IValueFormatter, the input to the FormatValue() method is the actual value. (Okay, technically, it's a ResolutionContext object that lets you get at the value using the SourceValue property, but you get the idea).

  • With a ValueResolver, the input to the ResolveCore() method is the entire source (not just the source value).

So, if you want to do some kind of conversion between a source value and a destination value, an IValueFormatter will only work if the destination type is a string, and a ValueResolver will only work if the ResolveCore() method "knows" which property to use (which won't be the case if your resolver is general purpose, i.e., doesn't apply to a particular property).

Workaround

Fortunately, there is always MapFrom, which provides the flexibility that is lacking with both resolvers and formatters.

Converter interface

I ended up writing an interface to simply and flexibly handle what I believe is a very common scenario: two-way conversions...

public interface ITwoWayConverter<TInput, TOutput>
{
    TOutput ConvertForward(TInput value);
    TInput ConvertBackward(TOutput value);
}

Example converter:

public class PhoneNumberToString : ITwoWayConverter<long, string>
{
    public string ConvertForward(long value)
    {
        return string.Format("{0:(###) ###-####}", Convert.ToInt64(value));
    }

    public long ConvertBackward(string value)
    {
        return Convert.ToInt64(Regex.Replace(value, @"\D", string.Empty));
    }
}

Example usage:

Mapper.CreateMap<User, UserViewModel>()
    .ForMember(dest => dest.PhoneNumber, opt => opt
        .MapFrom(orig => new PhoneNumberToString().ConvertForward(orig.PhoneNumber)));
like image 31
devuxer Avatar answered Nov 10 '22 07:11

devuxer