在哪里放置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()
现在,您可以将您的映射类放在对您有意义的位置,而不用担心一个单一的映射类。
对于那些坚持以下的人:
- 使用一个ioc容器
- 不喜欢为此打破封闭
- 不喜欢一个单一的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_start
和app_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);