asp.net-mvc 在哪里放置 AutoMapper.CreateMaps?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6825244/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Where to place AutoMapper.CreateMaps?
提问by Shawn Mclean
I'm using AutoMapperin an ASP.NET MVCapplication. I was told that I should move the AutoMapper.CreateMapelsewhere as they have a lot of overhead. I'm not too sure how to design my application to put these calls in just 1 place.
我AutoMapper在一个ASP.NET MVC应用程序中使用。有人告诉我,我应该把它搬到AutoMapper.CreateMap别处,因为他们有很多开销。我不太确定如何设计我的应用程序以将这些调用放在 1 个地方。
I have a web layer, service layer and a data layer. Each a project of its own. I use Ninjectto DI everything. I'll utilize AutoMapperin both web and service layers.
我有一个网络层、服务层和一个数据层。每一个都是自己的项目。我Ninject习惯于 DI 一切。我将AutoMapper在 Web 和服务层中使用。
So what are your setup for AutoMapper's CreateMap? Where do you put it? How do you call it?
那么您对AutoMapperCreateMap 的设置是什么?你把它放在哪里?你怎么称呼它?
回答by RPM1984
Doesn't matter, as long as it's a static class. It's all about convention.
没关系,只要它是一个静态类。这都是关于约定的。
Our conventionis that each "layer" (web, services, data) has a single file called AutoMapperXConfiguration.cs, with a single method called Configure(), where Xis the layer.
我们的约定是每个“层”(网络、服务、数据)都有一个名为 的文件AutoMapperXConfiguration.cs,有一个名为 的方法Configure(),X层在哪里。
The Configure()method then calls privatemethods for each area.
Configure()然后该private方法为每个区域调用方法。
Here's an example of our web tier config:
这是我们的 Web 层配置示例:
public static class AutoMapperWebConfiguration
{
public static void Configure()
{
ConfigureUserMapping();
ConfigurePostMapping();
}
private static void ConfigureUserMapping()
{
Mapper.CreateMap<User,UserViewModel>();
}
// ... etc
}
We create a method for each "aggregate" (User, Post), so things are separated nicely.
我们为每个“聚合”(用户、帖子)创建了一个方法,因此可以很好地分离事物。
Then your Global.asax:
那么你的Global.asax:
AutoMapperWebConfiguration.Configure();
AutoMapperServicesConfiguration.Configure();
AutoMapperDomainConfiguration.Configure();
// etc
It's kind of like an "interface of words" - can't enforce it, but you expect it, so you can code (and refactor) if necessary.
它有点像“文字界面”——不能强制执行,但你期望它,所以你可以在必要时编码(和重构)。
EDIT:
编辑:
Just thought I'd mention that I now use AutoMapper profiles, so the above example becomes:
只是想我会提到我现在使用 AutoMapper profiles,所以上面的例子变成:
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>();
}
}
Much cleaner/more robust.
更清洁/更坚固。
回答by Brett Allred
You can really put it anywhere as long as your web project references the assembly that it is in. In your situation I would put it in the service layer as that will be accessible by the web layer and the service layer and later if you decide to do a console app or you are doing a unit test project the mapping configuration will be available from those projects as well.
你真的可以把它放在任何地方,只要你的 web 项目引用它所在的程序集。在你的情况下,我会把它放在服务层中,因为 web 层和服务层可以访问它,以后如果你决定做一个控制台应用程序或者你正在做一个单元测试项目,映射配置也可以从这些项目中获得。
In your Global.asax you will then call the method that sets all of your maps. See below:
在您的 Global.asax 中,您将调用设置所有地图的方法。见下文:
File AutoMapperBootStrapper.cs
文件 AutoMapperBootStrapper.cs
public static class AutoMapperBootStrapper
{
public static void BootStrap()
{
AutoMapper.CreateMap<Object1, Object2>();
// So on...
}
}
Global.asax on application start
应用程序启动时的 Global.asax
just call
打电话
AutoMapperBootStrapper.BootStrap();
Now some people will argue against this method violates some SOLID principles, which they have valid arguments. Here they are for the reading.
现在有些人会反对这种方法违反了一些 SOLID 原则,他们有有效的论据。他们在这里阅读。
Configuring Automapper in Bootstrapper violates Open-Closed Principle?
回答by codeprogression
Update:The approach posted here is no more valid as SelfProfilerhas been removed as of AutoMapper v2.
更新:此处发布的方法不再有效,因为SelfProfiler已从 AutoMapper v2 中删除。
I would take a similar approach as Thoai. But I would use the built-in SelfProfiler<>class to handle the maps, then use the Mapper.SelfConfigurefunction to initialize.
我会采取与 Thoai 类似的方法。但是我会使用内置SelfProfiler<>类来处理地图,然后使用该Mapper.SelfConfigure函数进行初始化。
Using this object as the source:
使用这个对象作为源:
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);
}
}
And these as the destination:
这些作为目的地:
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; }
}
You can create these profiles:
您可以创建这些配置文件:
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));
}
}
To initialize in your application, create this class
要在您的应用程序中进行初始化,请创建此类
public class AutoMapperConfiguration
{
public static void Initialize()
{
Mapper.Initialize(x=>
{
x.SelfConfigure(typeof (UserViewModel).Assembly);
// add assemblies as necessary
});
}
}
Add this line to your global.asax.cs file: AutoMapperConfiguration.Initialize()
将此行添加到您的 global.asax.cs 文件中: AutoMapperConfiguration.Initialize()
Now you can place your mapping classes where they make sense to you and not worry about one monolithic mapping class.
现在,您可以将映射类放置在对您有意义的地方,而不必担心一个单一的映射类。
回答by Marius
For those of you who adhere to the following:
对于那些遵守以下规定的人:
- using an ioc container
- don't like to break open closed for this
- don't like a monolithic config file
- 使用 ioc 容器
- 不喜欢为此打开关闭
- 不喜欢单一的配置文件
I did a combo between profiles and leveraging my ioc container:
我在配置文件和利用我的 ioc 容器之间做了一个组合:
IoC configuration:
IOC配置:
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 example:
配置示例:
public class TagStatusViewModelMappings : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Service.Contracts.TagStatusViewModel, TagStatusViewModel>();
}
}
Usage example:
用法示例:
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));
}
}
The trade-off is that you have to reference the Mapper by the IMappingEngine interface instead of the static Mapper, but that's a convention I can live with.
权衡是您必须通过 IMappingEngine 接口而不是静态 Mapper 来引用 Mapper,但这是我可以接受的约定。
回答by ravy amiry
All of above solutions provide a static method to call (from app_start or any where) that it should call other methods to configure parts of mapping-configuration. But, if you have a modular application, that modules may plug in and out of application at any time, these solutions does not work. I suggest using WebActivatorlibrary that can register some methods to run on app_pre_startand app_post_startany where:
以上所有解决方案都提供了一个静态方法来调用(从 app_start 或任何地方)它应该调用其他方法来配置部分映射配置。但是,如果您有一个模块化应用程序,该模块可能随时插入和拔出应用程序,这些解决方案将不起作用。我建议使用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...
You can install WebActivatorvia NuGet.
您可以WebActivator通过 NuGet安装。
回答by Mahmoud Moravej
In addition to the best answer, a good way is using AutofacIoC liberary to add some automation. With this you justdefine your profiles regardless of initiations.
除了最佳答案之外,一个好方法是使用AutofacIoC 库来添加一些自动化。有了这个,您只需定义您的配置文件,而不管启动如何。
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);
});
}
}
}
}
and calling this line in Application_Startmethod:
并在Application_Start方法中调用这一行:
MapperConfig.Configure();
The above code finds all Profilesub classes and initiate them automatically.
上面的代码查找所有Profile子类并自动启动它们。
回答by Van Thoai Nguyen
Putting all the mapping logic in 1 location is not a good practice for me. Because the mapping class will be extremely large and very hard to maintain.
将所有映射逻辑放在 1 个位置对我来说不是一个好习惯。因为映射类会非常大,很难维护。
I recommend put the mapping stuff together with the ViewModel class in the same cs file. You can easily navigate to the mapping definition you want following this convention. Moreover, while creating the mapping class, you can reference to the ViewModel properties faster since they are in the same file.
我建议将映射内容与 ViewModel 类放在同一个 cs 文件中。遵循此约定,您可以轻松导航到所需的映射定义。此外,在创建映射类时,您可以更快地引用 ViewModel 属性,因为它们位于同一文件中。
So your view model class will look like:
所以你的视图模型类将如下所示:
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>();
}
}
回答by Andrey Burykin
From new version of AutoMapper using static method Mapper.Map() is deprecated. So you can add MapperConfiguration as static property to MvcApplication (Global.asax.cs) and use it to create instance of Mapper.
从使用静态方法 Mapper.Map() 的新版 AutoMapper 开始,已弃用。因此,您可以将 MapperConfiguration 作为静态属性添加到 MvcApplication (Global.asax.cs) 并使用它来创建 Mapper 的实例。
App_Start
App_Start
public class MapperConfig
{
public static MapperConfiguration MapperConfiguration()
{
return new MapperConfiguration(_ =>
{
_.AddProfile(new FileProfile());
_.AddProfile(new ChartProfile());
});
}
}
Global.asax.cs
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
基础控制器.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
https://github.com/AutoMapper/AutoMapper/wiki/Migrating-from-static-API
回答by roland
For vb.net programmers using the new Version (5.x) of AutoMapper.
适用于使用 AutoMapper 新版本 (5.x) 的 vb.net 程序员。
Global.asax.vb:
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
Profiles:
简介:
Public Class UserProfile
Inherits AutoMapper.Profile
Protected Overrides Sub Configure()
Me.CreateMap(Of User, UserViewModel)()
End Sub
End Class
Mapping:
映射:
Dim ViewUser = MapperConfiguration.Map(Of UserViewModel)(User)
回答by jpgrassi
For those who are (lost) using:
对于那些(丢失)使用的人:
- WebAPI 2
- SimpleInjector 3.1
- AutoMapper 4.2.1 (With Profiles)
- 网络API 2
- 简单注射器 3.1
- AutoMapper 4.2.1(带配置文件)
Here's how I managed integrating AutoMapper in the "new way". Also, a Hugethanks to this answer(and question)
这是我如何以“新方式”管理集成 AutoMapper 。此外,一个巨大的感谢这个答案(和问题)
1 - Created a folder in the WebAPI project called "ProfileMappers". In this folder I place all my profiles classes which creates my mappings:
1 - 在 WebAPI 项目中创建一个名为“ProfileMappers”的文件夹。在这个文件夹中,我放置了所有创建我的映射的配置文件类:
public class EntityToViewModelProfile : Profile
{
protected override void Configure()
{
CreateMap<User, UserViewModel>();
}
public override string ProfileName
{
get
{
return this.GetType().Name;
}
}
}
2 - In my App_Start, I have a SimpleInjectorApiInitializer which configures my SimpleInjector container:
2 - 在我的 App_Start 中,我有一个 SimpleInjectorApiInitializer 来配置我的 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
3 - 启动.cs
//Just call the Initialize method on the SimpleInjector class above
var container = SimpleInjectorApiInitializer.Initialize(configuration);
4 - Then, in your controller just inject as usually a IMapper interface:
4 - 然后,在您的控制器中,像往常一样注入 IMapper 接口:
private readonly IMapper mapper;
public AccountController( IMapper mapper)
{
this.mapper = mapper;
}
//Using..
var userEntity = mapper.Map<UserViewModel, User>(entity);

