web-dev-qa-db-fra.com

Configuration d'AutoMapper 4.2 avec IoC intégré dans ASP.NET Core 1.0 MVC6

J'essaie de trouver la bonne façon de configurer AutoMapper dans le fichier Startup.cs de mon application, puis de l'utiliser dans toute mon application.

J'essaie d'utiliser cette documentation qui explique quelque peu comment donner à AutoMapper une sensation statique sans l'ancienne API statique. L'exemple utilise StructureMap.

Je voudrais savoir comment je peux faire quelque chose de similaire, mais dans une application Core 1.0 utilisant le conteneur de services intégré.

Je suppose que dans la fonction Configure, je configurerais AutoMapper, puis dans la fonction ConfigureServices, je l'ajouterais comme transitoire.

Je suppose qu'en fin de compte, la façon la plus propre et la plus appropriée de le faire est d'utiliser l'injection de dépendance. Voici ma tentative actuelle mais elle ne fonctionne pas:

Startup.cs

public IMapper Mapper { get; set; }
private MapperConfiguration MapperConfiguration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
     services.AddTransient<IMapper, Mapper>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    MapperConfiguration MapperConfiguration = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Product, ProductViewModel>().ReverseMap();
    });

    Mapper = MapperConfiguration.CreateMapper();
}

Dans mon contrôleur:

private IMapper _mapper { get; set; }
// Constructor
public ProductsController(IMapper mapper)
{
    _mapper = mapper;
}

public IActionResult Create(ProductViewModel vm)
{
    Product product = _mapper.Map<ProductViewModel, Product>(vm);
}

Ça ne marche pas du tout ... Je dois manquer une étape ou faire quelque chose de mal.

21
Blake Rivell

Cette réponse convient un peu plus à l'approche MVC 6 autour de la couche Controller:

J'ai migré d'AutoMapper 4.1.1 vers 4.2.0, j'ai eu quelques problèmes pour comprendre les subtilités mais j'y suis finalement arrivé.

J'ai d'abord séparé la génération du profil AutoMapper dans une nouvelle classe (voir ci-dessous) pour éviter de colmater la classe de démarrage.

using AutoMapper;
using YourModels;
using YourViewModels;

namespace YourNamespace
{
    public class AutoMapperProfileConfiguration : Profile
    {
        protected override void Configure()
        {
            CreateMap<Application, ApplicationViewModel>();
            CreateMap<ApplicationViewModel, Application>();
            ...
        }
    }
}

J'ai apporté les modifications suivantes à la classe de démarrage.

J'ai ajouté une variable de membre privé de type MapperConfiguration.

private MapperConfiguration _mapperConfiguration { get; set; }

Dans le constructeur de démarrage, j'ai ajouté le code suivant pour instancier mon nouveau profil AutoMapper.

_mapperConfiguration = new MapperConfiguration(cfg =>
{
    cfg.AddProfile(new AutoMapperProfileConfiguration());
});

Dans ConfigureServices() j'ai déposé mon nouveau profil AutoMapper dans un singleton.

services.AddSingleton<IMapper>(sp => _mapperConfiguration.CreateMapper());

Ce n'était alors qu'une opération simple pour lui injecter les contrôleurs concernés.

using AutoMapper;
using ...

namespace YourNamespace
{
    public class ApplicationsController : BaseController
    {
        [FromServices]
        private IMapper _mapper { get; set; }

        [FromServices]
        private IApplicationRepository _applicationRepository { get; set; }

        public ApplicationsController(
            IMapper mapper,
            IApplicationRepository applicationRepository)
        {
            _mapper = mapper;
            _applicationRepository = applicationRepository;
        }

        // GET: Applications
        public async Task<IActionResult> Index()
        {
            IEnumerable<Application> applications = await _applicationRepository.GetForIdAsync(...);

            if (applications == null)
                return HttpNotFound();

            List<ApplicationViewModel> viewModel = _mapper.Map<List<ApplicationViewModel>>(applications);

            return View(viewModel);
        }

        ...
}

Merci à Rexebin sur https://pintoservice.wordpress.com/2016/01/31/dependency-injection-for-automapper-4-2-in-asp-net-vnext-mvc-project/ pour son poste qui aide énormément.

37
Chris Dixon

Vous pouvez également utiliser le package d'extension du créateur de l'automappeur.

Vous pouvez transmettre une configuration, spécifier des assemblys à analyser ou ne rien transmettre et le laisser analyser les assemblys à partir du DependencyContext.

https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection

https://www.nuget.org/packages/AutoMapper.Extensions.Microsoft.DependencyInjection/

public void ConfigureServices(IServiceCollection services)
{
    //configure DI
    services.AddTransient<IFoo, Foo>();

    //Add automapper - scans for Profiles
    services.AddAutoMapper();
    //or specify
    services.AddAutoMapper(cfg =>
    {
        cfg.AddProfile<ViewModelProfile>();
        ...
    });
    ...
7
Stuart

Dans vos services de configuration, vous pouvez créer une instance de MapperConfiguration, puis créer vos cartes et les ajouter.

public void ConfigureServices(IServiceCollection services)
{     
    MapperConfiguration configuration = new MapperConfiguration(cfg =>
    {
       cfg.AddProfile<MappingProfile.Profile1>();
       cfg.AddProfile<MappingProfile.Profile2>();
    });

    services.AddInstance(typeof (IMapper), configuration.CreateMapper());
}

Ensuite, vous injectez simplement l'IMapper dans votre constructeur et votre carte

public class Handler
{
      private readonly ProfileContext _db;
      private readonly IMapper _mapper;

      public Handler(ProfileContext db, IMapper mapper)
      {
          _db = db;
          _mapper = mapper;
      }

      public void Handle(Profile1 request)
      {

          ProfileModel profile = _mapper.Map<Profile1, ProfileModel>(request);

          _db.Profiles.Add(profile);

          try
          {
              db.SaveChanges();
          }
          catch (Exception ex)
          {

              throw;
          }

          return profile;
      }
}
4
Nyegaard

La réponse publiée est une excellente solution, mais j'ai pensé que je vous ferais savoir comment je le fais actuellement avec Core 1.0 et AutoMapper v5.1.1. J'ai l'impression que c'est une approche très agréable et facile à comprendre.

Dans Startup.cs en haut de la méthode Configure, écrivez simplement le code suivant pour initialiser vos mappages:

Mapper.Initialize(config =>
{
     config.CreateMap<ProductViewModel, Product>().ReverseMap();
     config.CreateMap<CustomerViewModel, Customer>().ReverseMap();
});

Ensuite, dans votre contrôleur ou toute autre partie de votre code où vous devez mapper:

Mapper.Map<Product>(productViewModel);
3
Blake Rivell

J'essaie simplement de déplacer une API Web de .net 4.5 vers .net core et j'en avais besoin. Vous étiez très proche, car @DavidGouge a suggéré que vous deviez enregistrer l'instance de mappeur que vous avez créée dans l'IOC. La configuration d'AutoMapper doit donc être effectuée dans ConfigureServices. (Ont inclus dans la ligne ici, mais dans mon projet, il appelle une méthode dans une classe AutoMapperConfiguration, donc le démarrage reste aussi propre que possible, de même avec les enregistrements IOC). L'IMapper sera alors correctement rempli dans votre contrôleur.

Startup.cs devient

   public IMapper Mapper { get; set; }
   private MapperConfiguration MapperConfiguration { get; set; }
   public void ConfigureServices(IServiceCollection services)
   {
        MapperConfiguration MapperConfiguration = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Product, ProductViewModel>().ReverseMap();
        });

        Mapper = MapperConfiguration.CreateMapper();

        services.AddInstance(Mapper);
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        // AutoMapper Configuration moved out of here.
    }
0
Michael Ball