如何将服务注入AutoMapper?

时间:2016-05-24 18:20:54

标签: c# asp.net-mvc unity3d automapper inversion-of-control

我正在使用AutoMapper将Entity映射到ViewModel。该实体的一个属性是datetime。我想使用TimeZone将该日期时间转换为本地日期时间。 TimeZone存储在用户的会话对象中。我已经有ISessionManager来回复用户会话中的信息。 我不知道如何将此ISessionManager注入AutoMapper

在下面的代码中,如何将ISessionManager.CurrentTimeZone属性传递给FromUTCToLocal()方法?

    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());                
    }
   }

2 个答案:

答案 0 :(得分:4)

我认为这是一种反模式,因为地图选手应该是愚蠢的,因此你可能想要抵制在其中加入过多逻辑的诱惑。查询数据是您的域实现的责任,但是如果您在没有它的情况下进行管理(特别是不在扩展方法中),那么您就不使用ServiceLocator了。

也许你应该丰富你要映射的域对象,而不是在映射过程中尝试添加缺失的信息(TimeZoneInfo)。

答案 1 :(得分:0)

AutoMapper实际上并不是一个反模式,它实际上是为了准确处理你想要的东西,你只需要采取不同的方法(奇怪的是我解决了这个问题,同时也处理了一个UTC问题,其中我最初也是尝试了一种基于扩展方法的解决方案。)

您可以使用AutoMapper的ValueResolvers而不是使用扩展方法。根据这是否是特定于某个特定类的映射,或者它是某个特定数据类型的一般映射,您需要实现IValueResolver(对于前者)或IMemberValueResolver(对于后者)。

这是一个很好的资源,可以帮助你完成大部分工作: https://github.com/AutoMapper/AutoMapper/wiki/Custom-value-resolvers

没有解决的问题是注入旋转变压器。

一个例子是这样的:

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
    }
}

你的映射器会有这样的东西:

cfg.CreateMap<MyEntity, MyViewModel>()
    .ForMember(vm => vm.CreatedDateTime,
               y => y.ResolveUsing<DateTimeUtcResolver, DateTime>(entity => entity.CreatedOn))

从那里只记得用AutoMapper注册你的IOC容器,这样就可以确定ISessionManager的实现。这本身可能很棘手,但取决于你正在使用什么类型的容器(这个帮助了我很多,ymmv:Automapper and request specific resources