我在ASP.NET MVC 5框架的顶部有一个用sleep
编写的项目。我正在使用AutoMapper,将视图模型动态映射到实体模型,反之亦然。
实体模型上的所有DateTime属性都位于UTC时区,而我的视图模型上的DateTime属性位于已登录用户的本地TimeZone中。
当我从实体模型映射到视图模型时,我需要将DateTime从UTC时区转换为登录用户的本地时区。为了使我的时间转换过程标准化,我有一个处理时间转换的服务类,称为C#
。转换器类具有依赖关系,并且其方法不是静态调用。
通常,我有一个mapper / factory类,该类使我可以将创建对象的过程分组到一个位置。
这是我的mapper / factory类的一个例子
DateTimeConverter
这是我的public CategoryMapper
{
private IMapper Mapper;
private IDateTimeConverter TimeConverter;
private ICategoryService CategoryService;
// There services are auto injected from the IoC
public MapperService(IMapper mapper, IDateTimeConverter timeConverter, ICategoryService categoryService)
{
Mapper = mapper;
TimeConverter = timeConverter;
CategoryService = categoryService;
}
public ListCategoriesViewModel GetListCategoriesViewModel()
{
var viewModel = new ListCategoriesViewModel();
// Use AutoMapper to create the viewmodels
var categories = Mapper.Map<List<DisplayCategoryViewModel>>(CategoryService.GetAll());
// I am trying to avoid having to do this loop each time...
// Hoping that somehome this can be adding in tho the previous call
// or add some kind of converter to AutoMapper to call TimeConverter.UtcToLocal on ALL DateTime properties
categories.ForEach(category =>
{
category.CreatedAt = TimeConverter.UtcToLocal(category.CreatedAt);
category.UpdatedAt = TimeConverter.UtcToLocal(category.UpdatedAt);
});
ViewModel.Categories = categories;
return viewModel;
}
}
班
DisplayVategoryViewModel
如上所述,我首先调用public class DisplayCategoryViewModel : IMapFrom
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
...
...
public void Map(IMapperConfigurationExpression expression)
{
// This method is called from the mapping profile when the app starts using reflection
// This way I can keep the mapping rules close to my viewmodel instead of having to move
// these rules into the mapper class directly.
// It is much easier to have the rules and the object in the same place when adding or renaming properties
expression.CreateMap<Category, DisplayCategoryViewModel>()
.ForMember(viewModel => viewModel.Name, opts => opts.MapFrom(model => model.Title));
}
}
来告诉AutoMapper使用映射的配置文件来映射对象。但是映射的配置文件不知道必须使用Mapper.Map<List<DisplayCategoryViewModel>>
实现来转换DateTime属性。因此,所有DateTime值均按原样映射,这意味着它们仍处于UTC TimeZone中。为了转换DateTime值,我在调用IDateTimeConverter
方法,该方法再次遍历每条记录以修复DateTime属性。
此外,我所有的ViewModel都是简单的DTO,所以我不想在其中添加任何逻辑。我不想在我的任何DTC的构造函数中注入任何东西。
我希望能够向Mapping配置添加某种规则,以告诉它将任何DateTime属性转换为UTC或本地时区。或将回调传递给ForEach
,以便我可以传递在映射过程中应用的自定义逻辑,以避免不必在映射的记录上创建另一个循环。
答案 0 :(得分:0)
您可以使用custom value resolver。
首先创建一个取决于您的IDateTimeConverter
服务的自定义解决方案
public class LocalToUtcResolver : IMemberValueResolver<object, object, DateTime, DateTime>, IMemberValueResolver<object, object, DateTime?, DateTime?>
{
private IDateTimeConverter TimeConverter;
public LocalToUtcResolver(IDateTimeConverter timeConverter)
{
TimeConverter = timeConverter;
}
public DateTime Resolve(object source, object destination, DateTime sourceMember, DateTime destMember, ResolutionContext context)
{
return TimeConverter.LocalToUtc(sourceMember);
}
public DateTime? Resolve(object source, object destination, DateTime? sourceMember, DateTime? destMember, ResolutionContext context)
{
return TimeConverter.LocalToUtc(sourceMember);
}
}
然后按照以下说明更新地图配置
public void Map(IMapperConfigurationExpression expression)
{
expression.CreateMap<Category, DisplayCategoryViewModel>()
.ForMember(viewModel => viewModel.Name, opts => opts.MapFrom(model => model.Title))
.ForMember(viewModel => viewModel.CreatedAt, opts => opts.ResolveUsing<UtcToLocalResolver, DateTime>(model => model.CreatedAt))
.ForMember(viewModel => viewModel.UpdatedAt, opts => opts.ResolveUsing<UtcToLocalResolver, DateTime?>(model => model.UpdatedAt));
}
要让IDateTimeConverter
在自定义解析器中解析,请确保为AutoMapper配置的服务未解析为默认服务,否则将无法正确解析依赖项。
这是我如何使用Unity-Container在我的一个项目中设置解析器的示例
var mapperConfig = new MapperConfiguration(expression =>
{
expression.AddProfile(...);
expression.ConstructServicesUsing(type => container.Resolve(type));
//...
//...
});
// Register singleton instance of the mapper class
container.RegisterInstance(mapperConfig.CreateMapper(), new
ContainerControlledLifetimeManager());