18

I'm trying to have AutoMapper take care of localizing all DateTime properties on our view models for us. We use UTC everywhere in our system and store everything in UTC in the database, but we'd like to automatically convert that to a user's time zone for display.

After looking at all the options, I settled on using a ValueResolver. Here's the gist of the resolver:

public class LocalizedDateTimeFormatter : ValueResolver<DateTime, DateTime>
{
    protected override DateTime ResolveCore(DateTime source)
    {
        // get company

        return company.TimeZone.ConvertFromUtc(source);
    }
}

I'm setting up the mapping like so:

Mapper.CreateMap<Entity, Model>()
    .ForMember(dest => dest.Foo, opt => opt.ResolveUsing<LocalizedDateTimeFormatter>()
                                            .FromMember(src => src.Foo));

This all works fine, and I'm happy with it. However, ideally we'd like a convention of all DateTime properties on a view model to use this resolver by default. I started down the path of reflecting over the view model properties, picking out DateTime ones, and using the overloads of ForMember and FromMember that take in property string names, but that seemed... ugly. Plus duplicating AutoMapper's nested property name building logic would break down pretty quick.

Question: Is there any easy way to tell AutoMapper to globally use a ValueResolver like this? To say "any time you're mapping a DateTime property on a source to a DateTime property on a destination, use this resolver"?

I looked through AutoMapper's tests and didn't see anything that'd work.

Thanks!

Darrell Mozingo
  • 865
  • 10
  • 16
  • This is a great question and I'm it will be a common requirement. I've had no luck trying to implement as per Jimmy's answer. Anyone know of a working example to follow? – Rob Bowman Jul 22 '15 at 10:57

1 Answers1

15

Yes - but with a slight change in ordering of the MapperRegistry. First, create a type converter from DateTime to DateTime:

Mapper.CreateMap<DateTime, DateTime>().ConvertUsing<CompanyTimeConverter>();

Your CompanyTimeConverter code looks pretty much like the value resolver you had, except it inherits from TypeConverter.

Next, you have to change the order of the MapperRegistry (I'm going to change this going forward, it makes more sense):

MapperRegistry.AllMappers = () => new IObjectMapper[] {
    new DataReaderMapper(),
    new TypeMapMapper(TypeMapObjectMapperRegistry.AllMappers()),
    new StringMapper(),
    new FlagsEnumMapper(),
    new EnumMapper(),
    new ArrayMapper(),
    new EnumerableToDictionaryMapper(),
    new DictionaryMapper(),
    new ListSourceMapper(),
    new CollectionMapper(),
    new EnumerableMapper(),
    new TypeConverterMapper(),
    new AssignableMapper(),
    new NullableMapper()
};

Originally, the "Assignable" mapper came before the "TypeConverter" mapper, so that if two types were assignable to each other, it would just do that.

Jimmy Bogard
  • 26,045
  • 5
  • 74
  • 69
  • 1
    Awesome, thanks Jimmy! I actually didn't need to change the order of the MapperRegistry to get it working though. I'm running AutoMapper 1.1.0.188. – Darrell Mozingo Apr 15 '11 at 18:20
  • is there a way to do it on a profile level? I don't think that changes to MapperRegistry can be isolated – chester89 Oct 16 '12 at 06:47
  • 1
    What if you're using AutoMapper to map both from entity to view model and then back from view model to entity (like one would do when editing an entity)? How can you tell AutoMapper to only map in one direction, or to use ConvertFromUtc in one direction and ConvertToUtc in the other? – Farinha Oct 24 '14 at 16:23
  • @Jimmy Bogard : How to create map for enum. In IQueryable case. – Hemant Malpote Jan 09 '15 at 08:19