我在ASP中使用AutoMapper。NET MVC应用程序。有人告诉我,我应该把自动取款机移走。在其他地方创建地图,因为他们有很多开销。我不太确定如何设计我的应用程序,把这些调用放在一个地方。

我有一个网络层,服务层和数据层。每个项目都有自己的特色。我用Ninject来DI所有东西。我将在web层和服务层使用AutoMapper。

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


当前回答

以上所有解决方案都提供了一个静态方法来调用(从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...

你可以通过NuGet安装WebActivator。

其他回答

更新:这里发布的方法不再有效,因为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文件中:

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

对于那些坚持以下原则的人:

使用ioc容器 我可不想因为这个破门而入 不喜欢单一的配置文件

我在配置文件和利用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,但这是我可以接受的约定。

对于(丢失)使用的人:

WebAPI 2 SimpleInjector 3.1 AutoMapper 4.2.1(带配置文件)

以下是我如何以“新方式”集成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);

将所有映射逻辑放在一个位置对我来说不是一个好的实践。因为映射类非常大,很难维护。

我建议把映射的东西和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>();
    }
}

除了最好的答案,一个好方法是使用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子类并自动初始化它们。