Answers:
没关系,只要它是静态类即可。一切都与惯例有关。
我们的约定是,每个“层”(Web,服务,数据)都有一个名为的文件AutoMapperXConfiguration.cs
,并带有一个名为的方法Configure()
,其中X
层。
Configure()
然后,该private
方法为每个区域调用方法。
这是我们的网络层配置的示例:
public static class AutoMapperWebConfiguration
{
public static void Configure()
{
ConfigureUserMapping();
ConfigurePostMapping();
}
private static void ConfigureUserMapping()
{
Mapper.CreateMap<User,UserViewModel>();
}
// ... etc
}
我们为每个“聚合”(用户,发布)创建一个方法,因此可以很好地分离事物。
然后您的Global.asax
:
AutoMapperWebConfiguration.Configure();
AutoMapperServicesConfiguration.Configure();
AutoMapperDomainConfiguration.Configure();
// etc
它有点像“单词的界面”-无法强制执行,但是您希望得到它,因此可以在必要时进行编码(和重构)。
编辑:
只是以为我提到我现在使用AutoMapper 配置文件,因此上面的示例变为:
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>();
}
}
更清洁/更坚固。
Mapper.Initialize
在每个Configuration类中调用是否会覆盖之前添加的配置文件?如果是这样,应该使用什么代替“初始化”?
Mapper.CreateMap()
,现在已过时。 'Mapper.Map<TSource, TDestination>(TSource, TDestination)' is obsolete: 'The static API will be removed in version 5.0. Use a MapperConfiguration instance and store statically as needed. Use CreateMapper to create a mapper instance.'
。您将如何更新示例以符合新要求?
只要您的Web项目引用了它所在的程序集,就可以将它放到任何地方。根据您的情况,我会将其放在服务层中,因为Web层和服务层将可以访问它,如果您决定做一个控制台应用程序,或者您正在做一个单元测试项目,映射配置也可以从那些项目中获得。
然后,在Global.asax中,您将调用设置所有地图的方法。见下文:
文件AutoMapperBootStrapper.cs
public static class AutoMapperBootStrapper
{
public static void BootStrap()
{
AutoMapper.CreateMap<Object1, Object2>();
// So on...
}
}
应用程序启动时的Global.asax
只是打电话
AutoMapperBootStrapper.BootStrap();
现在,有些人会反对这种方法违反了一些SOLID原则,这些原则具有有效的论据。他们在这里供阅读。
更新:此处发布的方法不再有效,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; }
}
您可以创建以下配置文件:
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));
}
}
要在您的应用程序中初始化,请创建此类
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容器之间进行了组合:
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;
}));
}
}
配置示例:
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接口而不是静态Mapper来引用Mapper,但这是我可以接受的约定。
上述所有解决方案都提供了一个静态方法来调用(从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...
您可以WebActivator
通过NuGet 安装。
MyModule1
项目(或项目的名称)中,只需创建一个名为的类并将InitMapInModule1
代码放入文件中即可;对于其他模块,请执行相同操作。
除了最佳答案外,一种好方法是使用Autofac IoC库添加一些自动化功能。这样,您就可以定义个人资料,而无需进行任何初始化。
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();
上面的代码查找所有Profile子类并自动启动它们。
对我而言,将所有映射逻辑放在一个位置不是一个好习惯。因为映射类将非常大并且很难维护。
我建议将映射内容与ViewModel类一起放在同一CS文件中。您可以按照此约定轻松导航至所需的映射定义。此外,在创建映射类时,由于它们位于同一文件中,因此可以更快地引用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),并使用它来创建Mapper实例。
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的方法。此外,一个巨大的感谢这个答案(和问题)
1-在WebAPI项目中创建了一个名为“ ProfileMappers”的文件夹。在此文件夹中,我放置了所有创建我的映射的配置文件类:
public class EntityToViewModelProfile : Profile
{
protected override void Configure()
{
CreateMap<User, UserViewModel>();
}
public override string ProfileName
{
get
{
return this.GetType().Name;
}
}
}
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
//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);
对于使用新版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)
Protected Overrides Sub Configure()
已弃用。一切保持不变,但此行应为:Public Sub New()