在哪里放置AutoMapper.CreateMaps?

Sha*_*ean 213 asp.net-mvc configuration design-patterns automapper

我使用AutoMapper中的ASP.NET MVC应用程序.有人告诉我,我应该搬到AutoMapper.CreateMap其他地方,因为他们有很多开销.我不太确定如何设计我的应用程序将这些调用放在一个地方.

我有一个Web层,服务层和数据层.每个项目都有自己的.我用NinjectDI来做一切.我将AutoMapper在网络和服务层使用.

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

RPM*_*984 218

没关系,只要它是一个静态类.这都是关于惯例的.

我们的惯例是每个"层"(Web,服务,数据)都有一个名为的文件,调用AutoMapperXConfiguration.cs一个方法Configure(),其中X是层.

Configure()然后该private方法为每个区域调用方法.

以下是我们的Web层配置示例:

public static class AutoMapperWebConfiguration
{
   public static void Configure()
   {
      ConfigureUserMapping();
      ConfigurePostMapping();
   }

   private static void ConfigureUserMapping()
   {
      Mapper.CreateMap<User,UserViewModel>();
   } 

   // ... etc
}
Run Code Online (Sandbox Code Playgroud)

我们为每个"聚合"(用户,帖子)创建一个方法,因此事情很好地分开.

然后你的Global.asax:

AutoMapperWebConfiguration.Configure();
AutoMapperServicesConfiguration.Configure();
AutoMapperDomainConfiguration.Configure();
// etc
Run Code Online (Sandbox Code Playgroud)

它有点像"单词界面" - 不能强制执行它,但是你期望它,所以你可以在必要时编码(和重构).

编辑:

我想我现在提到我现在使用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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

更清洁/更强大.

  • 从AutoMapper 4.2开始,`Mapper.CreateMap()`现在已经过时了.`'Mapper.Map <TSource,TDestination>(TSource,TDestination)'已过时:'静态API将在5.0版中删除.使用MapperConfiguration实例并根据需要静态存储.使用CreateMapper创建映射器实例.您如何更新您的示例以符合新要求? (19认同)
  • 在每个Configuration类中调用`Mapper.Initialize`会覆盖以前添加的配置文件吗?如果是这样,应该使用什么而不是Initialize? (7认同)
  • 这是否会使您的Web API项目引用您的服务和域层? (4认同)
  • 如果我有Web - >服务 - > BLL - > DAL.我的实体在我的DAL中.我不想从网络或服务中提及我的DAL.我该如何初始化它? (3认同)
  • @AliRızaAdıyahşi两个项目都应该有一个映射文件.核心应该有AutoMapperCoreConfiguration,UI应该有AutoMapperWebConfiguration.Web配置应添加Core配置中的配置文件. (2认同)
  • 这个问题是IMapper实例是在你的网站/ api项目和架构中创建的,我的域或服务项目不知道我的网站/ api项目我无法访问IMapper实例来执行我​​的域项目中的任何映射.在我看来,我必须创建IMapper的单独静态实例...我错过了什么吗? (2认同)
  • @ MatthewT.Baker你只能使用CreateMap <User,UserDTO>(); 没有"Mapper".https://github.com/AutoMapper/AutoMapper/wiki/Configuration (2认同)

Bre*_*red 33

只要您的Web项目引用它所在的程序集,您就可以将其放在任何位置.在您的情况下,我会将其放在服务层中,因为Web层和服务层可以访问它,如果您决定做一个控制台应用程序或者您正在进行单元测试项目,也可以从这些项目中获得映射配置.

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

文件AutoMapperBootStrapper.cs

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


     }
}
Run Code Online (Sandbox Code Playgroud)

应用程序启动时的Global.asax

只是打电话

AutoMapperBootStrapper.BootStrap();
Run Code Online (Sandbox Code Playgroud)

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

在Bootstrapper中配置Automapper违反了开放封闭原则?

  • 这个.朝着适当的"硬核"架构迈出的每一步似乎都涉及到指数级的代码.这很简单; 99.9%的编码员就足够了; 而你的同事会欣赏这种简约.是的,每个人都应该阅读有关开放 - 封闭原则的问题,但每个人都应该考虑权衡. (13认同)

cod*_*ion 16

更新:此处发布的方法不再有效,因为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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这些作为目的地:

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; }
}
Run Code Online (Sandbox Code Playgroud)

您可以创建这些配置文件:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

要在应用程序中初始化,请创建此类

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

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

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

  • 仅供参考,自Automapper v2以来,SelfProfiler类已经消失. (3认同)

Mar*_*ius 15

对于那些坚持以下内容的人:

  1. 使用ioc容器
  2. 不喜欢为此打破封闭
  3. 不喜欢单片配置文件

我在配置文件和利用我的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;
        }));
    }
}
Run Code Online (Sandbox Code Playgroud)

配置示例:

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

用法示例:

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)); 
    }
}
Run Code Online (Sandbox Code Playgroud)

权衡是您必须通过IMappingEngine接口而不是静态Mapper引用Mapper,但这是我可以使用的惯例.


jav*_*iry 14

以上所有解决方案都提供了一种静态方法来调用(来自app_start或任何其他地方)它应该调用其他方法来配置映射配置的部分.但是,如果您有模块化应用程序,那些模块可能随时插入和退出应用程序,这些解决方案不起作用.我建议使用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...
Run Code Online (Sandbox Code Playgroud)

您可以WebActivator通过NuGet 安装.

  • 我最近得出了同样的结论.它使您的地图创建代码接近消耗它的代码.此方法使MVC控制器更易于维护. (2认同)

Mah*_*vej 10

除了最好的答案,一个好方法是使用Autofac IoC liberary添加一些自动化.有了这个,您只需定义您的配置文件,无论启动如何.

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

            }

        }
    }
Run Code Online (Sandbox Code Playgroud)

并在Application_Start方法中调用此行:

MapperConfig.Configure();
Run Code Online (Sandbox Code Playgroud)

上面的代码找到所有Profile子类并自动启动它们.


Van*_*yen 7

将所有映射逻辑放在一个位置对我来说不是一个好习惯.因为映射类非常大并且很难维护.

我建议将映射内容与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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你怎么称呼它? (9认同)

And*_*kin 5

从新版本的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());
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

的Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    internal static MapperConfiguration MapperConfiguration { get; private set; }

    protected void Application_Start()
    {
        MapperConfiguration = MapperConfig.MapperConfiguration();
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

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