I am using AutoMapper for mapping Entity to ViewModel. One of the property of the entity is datetime. I wanted to convert that datetime to local datetime using the TimeZone. The TimeZone is stored in user's session object. I already have ISessionManager
which retrives information from User's session.
I am not sure how do i inject this ISessionManager into AutoMapper
In code below how do i pass ISessionManager.CurrentTimeZone
property into FromUTCToLocal() method?
public class SessionManager:ISessionManager
{
public TimeZoneInfo CurrentTimeZone
{
return (TimeZoneInfo)Session["CurrentTimeZone"];
}
}
public static class DateTimeExtenstions
{
public static DateTime FromLocalToUTC(this DateTime dateTime, TimeZoneInfo timeZone)
{
// Here instead of taking timeZone as parameter i can
// use servicelocator to get instance of ISessionManager
// like var timeZone = ServiceLocator.GetInstance<ISessionManager>()
// However i don't want to use servicelocator pattern since
// everywhere else i am using IOC
// also it doesnt make sense to use ServiceLocator inside extension method
return TimeZoneInfo.ConvertTimeToUtc(dateTime, timeZone);
}
}
public class AutoMapperConfig
{
public static IMapper Config()
{
var config = new MapperConfiguration(cfg =>
{
//Create all maps here
cfg.CreateMap<MyEntity, MyViewModel>()
.ForMember(vm => vm.CreatedDateTime,
y => y.MapFrom(entity => entity.CreatedOn.FromUTCToLocal()))
});
return config.CreateMapper();
}
}
public static class UnityConfig
{
public static void RegisterComponents(IMapper mapper)
{
var container = new UnityContainer();
container.RegisterType<ISessionManager, SessionManager>(new HierarchicalLifetimeManager());
container.RegisterInstance(mapper);
UnityServiceLocator locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);
}
}
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
UnityConfig.RegisterComponents(AutoMapperConfig.Config());
}
}
I think that's an anti-pattern, because mappers are supposed to be dumb, and thus you might want to resist the temptation of putting too much logic inside them. Querying data is the responsibility of your domain implementations, but you're right about not using the ServiceLocator if you've managed without it until now (especially not inside an extension method).
Perhaps you should enrich the domain object you're mapping from rather than trying to add missing information (TimeZoneInfo) during the mapping process.
Far from being an anti-pattern, AutoMapper was actually designed to handle exactly what you want, you'll just need to take a different approach (oddly enough I solved this while also working on a UTC problem in which I also initially attempted a solution based on extension methods).
Instead of using an extension method what you'll want is to utilize AutoMapper's ValueResolvers. Depending on if this is a mapping specific to a particular class or if it's a general mapping for a certain datatype you'll want to implement either IValueResolver (for the former) or IMemberValueResolver (for the latter).
This is a good resource that will get you most of the way there: https://github.com/AutoMapper/AutoMapper/wiki/Custom-value-resolvers
What that doesn't address is injecting into the resolver.
An example would be something like this:
public class DateTimeUtcResolver : IMemberValueResolver<object, object, DateTime, DateTime>
{
ISessionManager sessionManager;
public DateTimeUtcResolver(ISessionManager sessionManager)
{
this.sessionManager = sessionManager;
}
public DateTime Resolve(object source, object destination, DateTime sourceMember, DateTime destinationMember, ResolutionContext context)
{
//logic
}
}
Your mapper would then have something like this:
cfg.CreateMap<MyEntity, MyViewModel>()
.ForMember(vm => vm.CreatedDateTime,
y => y.ResolveUsing<DateTimeUtcResolver, DateTime>(entity => entity.CreatedOn))
From there just remember to register your IOC container with AutoMapper so that the implementation for ISessionManager can be determined. That itself can be tricky but will depend on what type of container you are using (this one helped me quite a bit, ymmv: Automapper and request specific resources)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With