在哪里放置AutoMapper.CreateMaps?

我在ASP.NET MVC应用程序中使用AutoMapper 。 有人告诉我应该把AutoMapper.CreateMap移到其他地方,因为它们有很多开销。 我不太清楚如何devise我的应用程序来把这些电话放在一个地方。

我有一个Web层,服务层和数据层。 每个项目都是自己的。 我用Ninject去DI一切。 我将在Web和服务层使用AutoMapper

那么你对AutoMapper的CreateMap有什么设置? 你把它放在哪里? 你怎么称呼它?

没关系,只要它是一个静态类。 这是关于公约的一切。

我们的惯例是每个“层”(web,services,data)都有一个名为AutoMapperXConfiguration.cs文件,其中一个叫做Configure()方法,其中X是图层。

Configure()方法然后调用每个区域的private方法。

以下是我们的Web层configuration示例:

 public static class AutoMapperWebConfiguration { public static void Configure() { ConfigureUserMapping(); ConfigurePostMapping(); } private static void ConfigureUserMapping() { Mapper.CreateMap<User,UserViewModel>(); } // ... etc } 

我们为每个“聚合”(User,Post)创build一个方法,所以事情很好地分离。

那么你的Global.asax

 AutoMapperWebConfiguration.Configure(); AutoMapperServicesConfiguration.Configure(); AutoMapperDomainConfiguration.Configure(); // etc 

这就像是一个“界面的话” – 不能执行它,但你期望它,所以你可以编码(和重构),如果有必要。

编辑:

只是想我会提到,我现在使用AutoMapper configuration文件 ,所以上面的例子变成:

 public static class AutoMapperWebConfiguration { public static void Configure() { Mapper.Initialize(cfg => { cfg.AddProfile(new UserProfile()); cfg.AddProfile(new PostProfile()); }); } } public class UserProfile : Profile { protected override void Configure() { Mapper.CreateMap<User,UserViewModel>(); } } 

更清洁/更强大。

只要你的web项目引用了它所在的程序集,你就可以把它放在任何地方。在你的情况下,我会把它放在服务层,因为web层和服务层可以访问它,如果你决定做一个控制台应用程序,或者你正在做一个unit testing项目,映射configuration也可以从这些项目中获得。

在您的Global.asax中,您将调用设置所有地图的方法。 见下文:

文件AutoMapperBootStrapper.cs

 public static class AutoMapperBootStrapper { public static void BootStrap() { AutoMapper.CreateMap<Object1, Object2>(); // So on... } } 

Global.asax应用程序启动

只是打电话

 AutoMapperBootStrapper.BootStrap(); 

现在有些人会反对这种方法违反了一些SOLID原则,他们有有效的论点。 在这里他们是为了阅读。

在Bootstrapper中configurationAutomapper违反开放原则?

更新:这里发布的方法不再有效,因为SelfProfiler已从AutoMapper v2中删除。

我会采取与Thoai类似的方法。 但是我会使用内置的SelfProfiler<>类来处理地图,然后使用Mapper.SelfConfigure函数进行初始化。

使用这个对象作为源代码:

 public class User { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } public string GetFullName() { return string.Format("{0} {1}", FirstName, LastName); } } 

而这些作为目的地:

 public class UserViewModel { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } public class UserWithAgeViewModel { public int Id { get; set; } public string FullName { get; set; } public int Age { get; set; } } 

您可以创build这些configuration文件:

 public class UserViewModelProfile : SelfProfiler<User,UserViewModel> { protected override void DescribeConfiguration(IMappingExpression<User, UserViewModel> map) { //This maps by convention, so no configuration needed } } public class UserWithAgeViewModelProfile : SelfProfiler<User, UserWithAgeViewModel> { protected override void DescribeConfiguration(IMappingExpression<User, UserWithAgeViewModel> map) { //This map needs a little configuration map.ForMember(d => d.Age, o => o.MapFrom(s => DateTime.Now.Year - s.BirthDate.Year)); } } 

要在你的应用程序中初始化,创build这个类

  public class AutoMapperConfiguration { public static void Initialize() { Mapper.Initialize(x=> { x.SelfConfigure(typeof (UserViewModel).Assembly); // add assemblies as necessary }); } } 

将这行添加到你的global.asax.cs文件中: AutoMapperConfiguration.Initialize()

现在,您可以将您的映射类放在对您有意义的位置,而不用担心一个单一的映射类。

对于那些坚持以下的人:

  1. 使用一个ioc容器
  2. 不喜欢为此打破封闭
  3. 不喜欢一个单一的configuration文件

我做了configuration文件之间的组合,并利用我的ioc容器:

IoCconfiguration:

 public class Automapper : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register(Classes.FromThisAssembly().BasedOn<Profile>().WithServiceBase()); container.Register(Component.For<IMappingEngine>().UsingFactoryMethod(k => { Profile[] profiles = k.ResolveAll<Profile>(); Mapper.Initialize(cfg => { foreach (var profile in profiles) { cfg.AddProfile(profile); } }); profiles.ForEach(k.ReleaseComponent); return Mapper.Engine; })); } } 

configuration举例:

 public class TagStatusViewModelMappings : Profile { protected override void Configure() { Mapper.CreateMap<Service.Contracts.TagStatusViewModel, TagStatusViewModel>(); } } 

用法示例:

 public class TagStatusController : ApiController { private readonly IFooService _service; private readonly IMappingEngine _mapper; public TagStatusController(IFooService service, IMappingEngine mapper) { _service = service; _mapper = mapper; } [Route("")] public HttpResponseMessage Get() { var response = _service.GetTagStatus(); return Request.CreateResponse(HttpStatusCode.Accepted, _mapper.Map<List<ViewModels.TagStatusViewModel>>(response)); } } 

权衡是你必须通过IMappingEngine接口而不是静态映射器来引用映射器,但是这是一个我可以接受的约定。

以上所有解决scheme都提供了一个静态方法来调用(从app_start或任何地方)它应该调用其他方法来configuration映射configuration的一部分。 但是,如果您有一个模块化应用程序,那么这些模块可能随时插入和退出应用程序,这些解决scheme不起作用。 我build议使用WebActivator库,可以注册一些方法来运行在app_pre_startapp_post_start任何地方:

 // in MyModule1.dll public class InitMapInModule1 { static void Init() { Mapper.CreateMap<User, UserViewModel>(); // other stuffs } } [assembly: PreApplicationStartMethod(typeof(InitMapInModule1), "Init")] // in MyModule2.dll public class InitMapInModule2 { static void Init() { Mapper.CreateMap<Blog, BlogViewModel>(); // other stuffs } } [assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")] // in MyModule3.dll public class InitMapInModule3 { static void Init() { Mapper.CreateMap<Comment, CommentViewModel>(); // other stuffs } } [assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")] // and in other libraries... 

您可以通过NuGet安装WebActivator

除了最好的答案之外,一个好方法是使用Autofac IoC自由版来增加一些自动化。 有了这个,你只需定义你的configuration文件,无论启动。

  public static class MapperConfig { internal static void Configure() { var myAssembly = Assembly.GetExecutingAssembly(); var builder = new ContainerBuilder(); builder.RegisterAssemblyTypes(myAssembly) .Where(t => t.IsSubclassOf(typeof(Profile))).As<Profile>(); var container = builder.Build(); using (var scope = container.BeginLifetimeScope()) { var profiles = container.Resolve<IEnumerable<Profile>>(); foreach (var profile in profiles) { Mapper.Initialize(cfg => { cfg.AddProfile(profile); }); } } } } 

并在Application_Start方法中调用这一行:

 MapperConfig.Configure(); 

上面的代码find所有Profile子类并自动启动它们。

把所有的映射逻辑放在1个位置对我来说不是一个好习惯。 因为地图类将会非常大,很难维护。

我build议把映射的东西和ViewModel类一起放在同一个cs文件中。 遵循这个约定,您可以轻松导航到您想要的映射定义。 而且,在创build映射类时,可以更快地引用ViewModel属性,因为它们在同一个文件中。

所以你的视图模型类将如下所示:

 public class UserViewModel { public ObjectId Id { get; set; } public string Firstname { get; set; } public string Lastname { get; set; } public string Email { get; set; } public string Password { get; set; } } public class UserViewModelMapping : IBootStrapper // Whatever { public void Start() { Mapper.CreateMap<User, UserViewModel>(); } } 

从新版本的AutoMapper使用静态方法Mapper.Map()已弃用。 因此,您可以将MapperConfiguration作为静态属性添加到MvcApplication(Global.asax.cs),并使用它创buildMapper的实例。

App_Start

 public class MapperConfig { public static MapperConfiguration MapperConfiguration() { return new MapperConfiguration(_ => { _.AddProfile(new FileProfile()); _.AddProfile(new ChartProfile()); }); } } 

的Global.asax.cs

 public class MvcApplication : System.Web.HttpApplication { internal static MapperConfiguration MapperConfiguration { get; private set; } protected void Application_Start() { MapperConfiguration = MapperConfig.MapperConfiguration(); ... } } 

BaseController.cs

  public class BaseController : Controller { // // GET: /Base/ private IMapper _mapper = null; protected IMapper Mapper { get { if (_mapper == null) _mapper = MvcApplication.MapperConfiguration.CreateMapper(); return _mapper; } } } 

https://github.com/AutoMapper/AutoMapper/wiki/Migrating-from-static-API

对于使用AutoMapper的新版本(5.x)的vb.net程序员。

Global.asax.vb:

 Public Class MvcApplication Inherits System.Web.HttpApplication Protected Sub Application_Start() AutoMapperConfiguration.Configure() End Sub End Class 

AutoMapperConfiguration:

 Imports AutoMapper Module AutoMapperConfiguration Public MapperConfiguration As IMapper Public Sub Configure() Dim config = New MapperConfiguration( Sub(cfg) cfg.AddProfile(New UserProfile()) cfg.AddProfile(New PostProfile()) End Sub) MapperConfiguration = config.CreateMapper() End Sub End Module 

简介:

 Public Class UserProfile Inherits AutoMapper.Profile Protected Overrides Sub Configure() Me.CreateMap(Of User, UserViewModel)() End Sub End Class 

制图:

 Dim ViewUser = MapperConfiguration.Map(Of UserViewModel)(User) 

对于那些(丢失)使用:

  • WebAPI 2
  • SimpleInjector 3.1
  • AutoMapper 4.2.1(configuration文件)

以下是我如何以“ 新方式 ”整合AutoMapper。 另外,非常感谢这个答案(和问题)

1 – 在WebAPI项目中创build一个名为“ProfileMappers”的文件夹。 在这个文件夹中,我放置了所有我的configuration文件类创build我的映射:

 public class EntityToViewModelProfile : Profile { protected override void Configure() { CreateMap<User, UserViewModel>(); } public override string ProfileName { get { return this.GetType().Name; } } } 

2 – 在我的App_Start中,我有一个SimpleInjectorApiInitializer,它configuration我的SimpleInjector容器:

 public static Container Initialize(HttpConfiguration httpConfig) { var container = new Container(); container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle(); //Register Installers Register(container); container.RegisterWebApiControllers(GlobalConfiguration.Configuration); //Verify container container.Verify(); //Set SimpleInjector as the Dependency Resolver for the API GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container); httpConfig.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container); return container; } private static void Register(Container container) { container.Register<ISingleton, Singleton>(Lifestyle.Singleton); //Get all my Profiles from the assembly (in my case was the webapi) var profiles = from t in typeof(SimpleInjectorApiInitializer).Assembly.GetTypes() where typeof(Profile).IsAssignableFrom(t) select (Profile)Activator.CreateInstance(t); //add all profiles found to the MapperConfiguration var config = new MapperConfiguration(cfg => { foreach (var profile in profiles) { cfg.AddProfile(profile); } }); //Register IMapper instance in the container. container.Register<IMapper>(() => config.CreateMapper(container.GetInstance)); //If you need the config for LinqProjections, inject also the config //container.RegisterSingleton<MapperConfiguration>(config); } 

3 – Startup.cs

 //Just call the Initialize method on the SimpleInjector class above var container = SimpleInjectorApiInitializer.Initialize(configuration); 

4 – 然后,在你的控制器只是注入通常IMapper接口:

 private readonly IMapper mapper; public AccountController( IMapper mapper) { this.mapper = mapper; } //Using.. var userEntity = mapper.Map<UserViewModel, User>(entity);