Cómo configurar Automapper en ASP.NET Core


255

Soy relativamente nuevo en .NET, y decidí abordar .NET Core en lugar de aprender las "viejas formas". Encontré un artículo detallado sobre cómo configurar AutoMapper para .NET Core aquí , pero ¿hay un tutorial más simple para un novato?



Para versiones más nuevas de core (> v1), consulte la respuesta de @ Saineshwar stackoverflow.com/a/53455699/833878
Robbie

1
Una respuesta completa con un ejemplo haga clic en este enlace
Iman Bahrampour

Respuestas:


554

¡Me lo imaginé! Aquí están los detalles:

  1. Agregue el paquete principal AutoMapper a su solución a través de NuGet .
  2. Agregue el paquete de inyección de dependencia de AutoMapper a su solución a través de NuGet .

  3. Cree una nueva clase para un perfil de mapeo. (Hice una clase en el directorio principal de la solución llamada MappingProfile.csy agregué el siguiente código). Usaré un objeto Usery UserDtocomo ejemplo.

    public class MappingProfile : Profile {
        public MappingProfile() {
            // Add as many of these lines as you need to map your objects
            CreateMap<User, UserDto>();
            CreateMap<UserDto, User>();
        }
    }
  4. Luego agregue la AutoMapperConfiguration en el Startup.csque se muestra a continuación:

    public void ConfigureServices(IServiceCollection services) {
        // .... Ignore code before this
    
       // Auto Mapper Configurations
        var mappingConfig = new MapperConfiguration(mc =>
        {
            mc.AddProfile(new MappingProfile());
        });
    
        IMapper mapper = mappingConfig.CreateMapper();
        services.AddSingleton(mapper);
    
        services.AddMvc();
    
    }
  5. Para invocar el objeto mapeado en código, haga algo como lo siguiente:

    public class UserController : Controller {
    
        // Create a field to store the mapper object
        private readonly IMapper _mapper;
    
        // Assign the object in the constructor for dependency injection
        public UserController(IMapper mapper) {
            _mapper = mapper;
        }
    
        public async Task<IActionResult> Edit(string id) {
    
            // Instantiate source object
            // (Get it from the database or whatever your code calls for)
            var user = await _context.Users
                .SingleOrDefaultAsync(u => u.Id == id);
    
            // Instantiate the mapped data transfer object
            // using the mapper you stored in the private field.
            // The type of the source object is the first type argument
            // and the type of the destination is the second.
            // Pass the source object you just instantiated above
            // as the argument to the _mapper.Map<>() method.
            var model = _mapper.Map<UserDto>(user);
    
            // .... Do whatever you want after that!
        }
    }

¡Espero que esto ayude a alguien que comienza de cero con ASP.NET Core! Agradezco cualquier comentario o crítica ya que todavía soy nuevo en el mundo .NET.


3
El artículo detallado vinculado, lostechies.com/jimmybogard/2016/07/20/… , explica cómo Profilese ubican las clases
Kieren Johnstone

22
@theutz Puede fusionar esas dos líneas CreateMap con un .ReverseMap () al final de, bien, bien. Quizás lo comente, pero lo encuentro más intuitivo.
Astravagrant

66
Puede ser útil en el Paso 3 mencionar agregar un "uso de AutoMapper"; en la parte superior para que se importe el método de extensión.
Rocklan

8
Esto funcionó bien con .net core 1.1, ya no una vez que actualicé a .net core 2.0. Creo que necesito especificar explícitamente el ensamblado de clase de perfil lógico. Todavía estoy investigando cómo lograr eso. Actualización: Ah, la respuesta reside en tu comentario, tengo que pasar el tipo de clase, que es mi perfil. // services.AddAutoMapper (typeof (Startup)); // <- la nueva versión de automapper usa esta firma
Esen

3
En AutoMapper v8 y el complemento Dependency Injection v5, lo único que se necesita son los servicios. AddAutoMapper (); línea en el método ConfigureServices de la clase Startup. Para mí, incluso pude encontrar clases de perfil en proyectos de biblioteca de clases dependientes.
stricq

69

Paso para usar AutoMapper con ASP.NET Core.

Paso 1. Instalar AutoMapper.Extensions.Microsoft.DependencyInjection desde el paquete NuGet.

ingrese la descripción de la imagen aquí

Paso 2. Cree una carpeta en solución para mantener las asignaciones con el nombre "Asignaciones".

ingrese la descripción de la imagen aquí

Paso 3. Después de agregar la carpeta Mapping, hemos agregado una clase con el nombre " MappingProfile ". Este nombre puede ser único y bueno de entender.

En esta clase, vamos a mantener todas las asignaciones.

ingrese la descripción de la imagen aquí

Paso 4. Inicializando Mapper en el inicio "ConfigureServices"

En Startup Class, necesitamos inicializar el perfil que hemos creado y también registrar el servicio AutoMapper.

  Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());

  services.AddAutoMapper();

Fragmento de código para mostrar el método ConfigureServices donde necesitamos inicializar y registrar AutoMapper.

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }


    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });


        // Start Registering and Initializing AutoMapper

        Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
        services.AddAutoMapper();

        // End Registering and Initializing AutoMapper

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    }}

Paso 5. Obtener salida.

Para obtener el resultado del mapeo, debemos llamar a AutoMapper.Mapper.Map y pasar el Destino y el Origen adecuados.

AutoMapper.Mapper.Map<Destination>(source);

Fragmento de código

    [HttpPost]
    public void Post([FromBody] SchemeMasterViewModel schemeMaster)
    {
        if (ModelState.IsValid)
        {
            var mappedresult = AutoMapper.Mapper.Map<SchemeMaster>(schemeMaster);
        }
    }

13
Me sale el siguiente error: 'Mapper' does not contain a definition for 'initialize'. Estoy usando la AutoMapper.Extensions.Microsoft.DependencyInjectionversión 7.0.0
kimbaudi

Respuesta super detallada. Gracias Señor.
Rod Hartzell

1
si está utilizando ASP.NET CORE 3.0, consulte este tutorial Cómo configurar AutoMapper en ASP.NET Core 3.0 tutexchange.com/how-to-set-up-automapper-in-asp-net-core-3-0
Saineshwar

44

Quiero extender las respuestas de @ theutz, es decir, esta línea:

// services.AddAutoMapper(typeof(Startup));  // <-- newer automapper version uses this signature.

Hay un error ( probablemente ) en AutoMapper.Extensions.Microsoft.DependencyInjection versión 3.2.0. (Estoy usando .NET Core 2.0)

Esto se aborda en este problema de GitHub. Si sus clases que heredan la clase Perfil de AutoMapper existen fuera del ensamblaje donde está su clase de Inicio, probablemente no se registrarán si su inyección de AutoMapper se ve así:

services.AddAutoMapper();

a menos que especifique explícitamente qué ensamblajes buscar en los perfiles de AutoMapper.

Se puede hacer así en su Startup.ConfigureServices:

services.AddAutoMapper(<assembies> or <type_in_assemblies>);

donde "ensamblados" y "type_in_assemblies" apuntan al ensamblado donde se especifican las clases de perfil en su aplicación. P.ej:

services.AddAutoMapper(typeof(ProfileInOtherAssembly), typeof(ProfileInYetAnotherAssembly));

Yo supongo (y pongo énfasis en esta palabra) que, debido a raíz de la aplicación de la sobrecarga sin parámetros (código fuente desde GitHub ):

public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
     return services.AddAutoMapper(null, AppDomain.CurrentDomain.GetAssemblies());
}

confiamos en que CLR ya ha ensamblado JIT que contiene perfiles de AutoMapper que pueden ser o no ciertos, ya que solo se modifican cuando es necesario (más detalles en esta pregunta de StackOverflow).


55
Esa es la respuesta correcta para la última versión de AutoMapper y AspNetCore
Joshit

1
esta fue la respuesta que estaba buscando para AutoMapper 8.1 (última versión)
Tinaira

30

La respuesta de theutz aquí es muy buena, solo quiero agregar esto:

Si deja que su perfil de mapeo herede en MapperConfigurationExpressionlugar de Profile, simplemente puede agregar una prueba para verificar su configuración de mapeo, que siempre es útil:

[Fact]
public void MappingProfile_VerifyMappings()
{
    var mappingProfile = new MappingProfile();

    var config = new MapperConfiguration(mappingProfile);
    var mapper = new Mapper(config);

    (mapper as IMapper).ConfigurationProvider.AssertConfigurationIsValid();
}

Recibo un error: "La inyección de dependencia de extensión de AutoMapper es incompatible con asp.net core 1.1". ¡Por favor ayuda!
Rohit Arora

Parece que la definición de "verificar" está en debate. Esto explota cuando el diseño omite ciertas propiedades para evitar el mapeo.
Jeremy Holovacs

2
Si no desea asignar una propiedad, configúrela con .Ignore (). De esta forma, te obliga a pensar activamente en el manejo de cada caso, asegurándote de no perderte nada cuando se realizan cambios. Súper práctico, en realidad. Entonces, sí, la prueba de verificación es una red de seguridad más grande de lo que muchas personas creen. No es infalible, pero se encarga del primer 90%.
Arve Systad el

18

Lo resolví de esta manera (similar a lo anterior pero siento que es una solución más limpia) para .NET Core 2.2 / Automapper 8.1.1 w / Extensions.DI 6.1.1.

Crear la clase MappingProfile.cs y completar el constructor con Maps (planeo usar una sola clase para contener todas mis asignaciones)

    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<Source, Dest>().ReverseMap();
        }
    }

En Startup.cs, agregue a continuación para agregar a DI (el argumento de ensamblaje es para la clase que contiene sus configuraciones de mapeo, en mi caso, es la clase MappingProfile).

//add automapper DI
services.AddAutoMapper(typeof(MappingProfile));

En Controller, úselo como lo haría con cualquier otro objeto DI

    [Route("api/[controller]")]
    [ApiController]
    public class AnyController : ControllerBase
    {
        private readonly IMapper _mapper;

        public AnyController(IMapper mapper)
        {
            _mapper = mapper;
        }

        public IActionResult Get(int id)
        {
            var entity = repository.Get(id);
            var dto = _mapper.Map<Dest>(entity);

            return Ok(dto);
        }
    }


2
Me gusta tu respuesta. Creo que es innecesario terminar MappingProfilescon lo new Type[]{}que se muestra en esta respuesta .
Hombre demasiado gordo sin cuello

10

En mi Startup.cs (Core 2.2, Automapper 8.1.1)

services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });            

En mi proyecto de acceso a datos

namespace DAL
{
    public class MapperProfile : Profile
    {
        // place holder for AddAutoMapper (to bring in the DAL assembly)
    }
}

En mi definición de modelo

namespace DAL.Models
{
    public class PositionProfile : Profile
    {
        public PositionProfile()
        {
            CreateMap<Position, PositionDto_v1>();
        }
    }

    public class Position
    {
        ...
    }

¿Por qué no solo usas en services.AddAutoMapper( typeof(DAL.MapperProfile) ); lugar de services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });?
Hombre demasiado gordo sin cuello

8

Me gustan muchas respuestas, particularmente la de @saineshwar. Estoy usando .net Core 3.0 con AutoMapper 9.0, así que siento que es hora de actualizar su respuesta.

Lo que funcionó para mí fue en Startup.ConfigureServices (...) registre el servicio de esta manera:

    services.AddAutoMapper(cfg => cfg.AddProfile<MappingProfile>(), 
                               AppDomain.CurrentDomain.GetAssemblies());

Creo que el resto de la respuesta de @saineshwar se mantiene perfecta. Pero si alguien está interesado, mi código de controlador es:

[HttpGet("{id}")]
public async Task<ActionResult> GetIic(int id)
{
    // _context is a DB provider
    var Iic = await _context.Find(id).ConfigureAwait(false);

    if (Iic == null)
    {
        return NotFound();
    }

    var map = _mapper.Map<IicVM>(Iic);

    return Ok(map);
}

Y mi clase de mapeo:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Iic, IicVM>()
            .ForMember(dest => dest.DepartmentName, o => o.MapFrom(src => src.Department.Name))
            .ForMember(dest => dest.PortfolioTypeName, o => o.MapFrom(src => src.PortfolioType.Name));
            //.ReverseMap();
    }
}

----- EDITAR -----

Después de leer los documentos vinculados en los comentarios de Lucian Bargaoanu, creo que es mejor cambiar un poco esta respuesta.

El sin parámetros services.AddAutoMapper()(que tenía la respuesta @saineshwar) ya no funciona (al menos para mí). Pero si usa el ensamblado NuGet AutoMapper.Extensions.Microsoft.DependencyInjection, el marco puede inspeccionar todas las clases que extienden AutoMapper.Profile (como el mío, MappingProfile).

Entonces, en mi caso, donde la clase pertenece al mismo ensamblaje en ejecución, el registro del servicio se puede acortar a services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(Un enfoque más elegante podría ser una extensión sin parámetros con esta codificación).

Gracias Lucian!



6

Estoy usando AutoMapper 6.1.1 y asp.net Core 1.1.2.

En primer lugar, defina las clases de perfil heredadas por la clase de perfil de Automapper. Creé la interfaz IProfile que está vacía, el propósito es solo encontrar las clases de este tipo.

 public class UserProfile : Profile, IProfile
    {
        public UserProfile()
        {
            CreateMap<User, UserModel>();
            CreateMap<UserModel, User>();
        }
    }

Ahora cree una clase separada, por ejemplo, asignaciones

 public class Mappings
    {
     public static void RegisterMappings()
     {            
       var all =
       Assembly
          .GetEntryAssembly()
          .GetReferencedAssemblies()
          .Select(Assembly.Load)
          .SelectMany(x => x.DefinedTypes)
          .Where(type => typeof(IProfile).GetTypeInfo().IsAssignableFrom(type.AsType()));

            foreach (var ti in all)
            {
                var t = ti.AsType();
                if (t.Equals(typeof(IProfile)))
                {
                    Mapper.Initialize(cfg =>
                    {
                        cfg.AddProfiles(t); // Initialise each Profile classe
                    });
                }
            }         
        }

    }

Ahora en MVC Core web Project en el archivo Startup.cs, en el constructor, llame a la clase Mapping que inicializará todas las asignaciones en el momento de la carga de la aplicación.

Mappings.RegisterMappings();

Puede crear una subclase a partir de la clase de perfil y cuando el programa esté ejecutando servicios. AddAutoMapper (); línea de códigos El automapper los conoce automáticamente.
isaeid

No creo que esto sea necesario si usa AutoMapper.Extensions.Microsoft.DependancyInjection que está disponible en nuget.
Greg Gum

5

Para ASP.NET Core (probado con 2.0+ y 3.0), si prefiere leer la documentación de origen: https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md

De lo contrario, seguir estos 4 pasos funciona:

  1. Instale AutoMapper.Extensions.Microsoft.DependancyInjection desde nuget.

  2. Simplemente agregue algunas clases de perfil.

  3. Luego agregue a continuación a su clase startup.cs. services.AddAutoMapper(OneOfYourProfileClassNamesHere)

  4. Luego, simplemente inyecte IMapper en sus controladores o donde lo necesite:

public class EmployeesController {

    private readonly IMapper _mapper;

    public EmployeesController(IMapper mapper){

        _mapper = mapper;
    }

Y si quieres usar ProjectTo es ahora simplemente:

var customers = await dbContext.Customers.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider).ToListAsync()

4

Para AutoMapper 9.0.0:

public static IEnumerable<Type> GetAutoMapperProfilesFromAllAssemblies()
    {
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (var aType in assembly.GetTypes())
            {
                if (aType.IsClass && !aType.IsAbstract && aType.IsSubclassOf(typeof(Profile)))
                    yield return aType;
            }
        }
    }

MapperProfile:

public class OrganizationProfile : Profile
{
  public OrganizationProfile()
  {
    CreateMap<Foo, FooDto>();
    // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
  }
}

En tu inicio:

services.AddAutoMapper(GetAutoMapperProfilesFromAllAssemblies()
            .ToArray());

En Controlador o servicio: Inyectar mapeador:

private readonly IMapper _mapper;

Uso:

var obj = _mapper.Map<TDest>(sourceObject);

4

En las últimas versiones de asp.net core, debe usar la siguiente inicialización:

services.AddAutoMapper(typeof(YourMappingProfileClass));

2

Asp.Net Core 2.2 con AutoMapper.Extensions.Microsoft.DependencyInjection.

public class MappingProfile : Profile
{
  public MappingProfile()
  {
      CreateMap<Domain, DomainDto>();
  }
}

En Startup.cs

services.AddAutoMapper(typeof(List.Handler));

1

Para agregar a lo que Arve Systad mencionó para las pruebas. Si por alguna razón eres como yo y quieres mantener la estructura de herencia proporcionada en la solución deutz, puedes configurar MapperConfiguration de esta manera:

var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(cfg =>
{
    cfg.AddProfile(mappingProfile);
});
var mapper = new Mapper(config);

Hice esto en NUnit.


1

servicios.AddAutoMapper (); No funcionó para mí. (Estoy usando Asp.Net Core 2.0)

Después de configurar como a continuación

   var config = new AutoMapper.MapperConfiguration(cfg =>
   {                 
       cfg.CreateMap<ClientCustomer, Models.Customer>();
   });

inicializar el mapeador IMapper mapper = config.CreateMapper ();

y agregue el objeto del mapeador a los servicios como servicios singleton. AddSingleton (mapeador);

de esta manera puedo agregar una DI al controlador

  private IMapper autoMapper = null;

  public VerifyController(IMapper mapper)
  {              
   autoMapper = mapper;  
  }

y he usado lo siguiente en mis métodos de acción

  ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);

Hola @venkat, probablemente solo necesites agregar el paquete AutoMapper.Extensions.Microsoft.DependancyInjection a tu proyecto
dalcam

-1

acerca de la respuesta thez, no hay necesidad de especificar el parámetro de mapeador IMapper en el constructor de controladores.

puede usar el asignador, ya que es un miembro estático en cualquier lugar del código.

public class UserController : Controller {
   public someMethod()
   {
      Mapper.Map<User, UserDto>(user);
   }
}

11
Pero las estadísticas son un poco contrastables, ¿no?
Scott Fraley

3
Sí. Esto funcionará en muchos casos, pero si no tiene una asignación configurada al invocar este método en una prueba, arrojará una excepción (y, por lo tanto, fallará la prueba por la razón incorrecta). Con un inyectado IMapperpuede burlarse de eso y, por ejemplo, simplemente hacer que devuelva nulo si es irrelevante para la prueba dada.
Arve Systad
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.