Глобально применять преобразователь значений с помощью AutoMapper
Я пытаюсь, чтобы AutoMapper позаботился о локализации всех свойств DateTime на наших моделях просмотра для нас. Мы используем UTC всюду в нашей системе и сохраняем все в UTC в базе данных, но мы хотели бы автоматически преобразовать это в часовой пояс пользователя для отображения.
Посмотрев на все варианты, я решил использовать ValueResolver. Здесь суть резольвера:
public class LocalizedDateTimeFormatter : ValueResolver<DateTime, DateTime>
{
protected override DateTime ResolveCore(DateTime source)
{
// get company
return company.TimeZone.ConvertFromUtc(source);
}
}
Я настраиваю отображение следующим образом:
Mapper.CreateMap<Entity, Model>()
.ForMember(dest => dest.Foo, opt => opt.ResolveUsing<LocalizedDateTimeFormatter>()
.FromMember(src => src.Foo));
Все это прекрасно работает, и я доволен этим. Однако в идеале мы бы хотели, чтобы по умолчанию все свойства DateTime в модели представления использовали для использования этого резольвера. Я начал путь к отражению свойств модели представления, выделению DateTime и использованию перегрузок ForMember и FromMember, которые принимают имена строк свойств, но это казалось... уродливым. Плюс дублирование логики построения вложенных свойств AutoMapper будет довольно быстро сломаться.
Вопрос: Есть ли простой способ сказать AutoMapper глобально использовать ValueResolver, как это? Чтобы сказать: "В любое время, когда вы сопоставляете свойство DateTime с источником в свойство DateTime в конечном месте, используйте этот преобразователь"?
Я просмотрел тесты AutoMapper и не видел ничего, что сработало бы.
Спасибо!
Ответы
Ответ 1
Да - но с небольшим изменением порядка MapperRegistry. Сначала создайте конвертер типов от DateTime до DateTime:
Mapper.CreateMap<DateTime, DateTime>().ConvertUsing<CompanyTimeConverter>();
Код вашей компанииTimeConverter выглядит так же, как и у распознавателя значений, кроме того, что он наследует от TypeConverter.
Затем вам нужно изменить порядок MapperRegistry (я собираюсь изменить это в будущем, это имеет смысл):
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()
};
Первоначально, "Assignable" mapper появился перед преобразователем TypeConverter, так что, если бы два типа были назначены друг другу, он просто сделал бы это.