Sou relativamente novo no .NET e decidi abordar o .NET Core em vez de aprender as "maneiras antigas". Encontrei um artigo detalhado sobre a configuração do AutoMapper para .NET Core aqui , mas há uma explicação mais simples para um novato?
Sou relativamente novo no .NET e decidi abordar o .NET Core em vez de aprender as "maneiras antigas". Encontrei um artigo detalhado sobre a configuração do AutoMapper para .NET Core aqui , mas há uma explicação mais simples para um novato?
Respostas:
Eu descobri! Aqui estão os detalhes:
Adicione o pacote de injeção de dependência do AutoMapper à sua solução via NuGet .
Crie uma nova classe para um perfil de mapeamento. (Fiz uma classe no diretório principal da solução chamada MappingProfile.cs
e adicionei o código a seguir.) Usarei um User
eUserDto
objeto como exemplo.
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>();
}
}
Em seguida, adicione o AutoMapperConfiguration no Startup.cs
como mostrado abaixo:
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();
}
Para invocar o objeto mapeado no código, faça algo como o seguinte:
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 isso ajude alguém a começar de novo com o ASP.NET Core! Congratulo-me com quaisquer comentários ou críticas, pois ainda sou novo no mundo .NET!
Profile
classes estão localizadas
Etapa para usar o AutoMapper com o ASP.NET Core.
Etapa 1. Instalando o AutoMapper.Extensions.Microsoft.DependencyInjection a partir do NuGet Package.
Etapa 2. Crie uma pasta na solução para manter os mapeamentos com o nome "Mapeamentos".
Etapa 3. Após adicionar a pasta Mapping, adicionamos uma classe com o nome " MappingProfile ", que esse nome pode ser único e bom de entender.
Nesta classe, vamos manter todos os mapeamentos.
Etapa 4. Inicializando o Mapper na Inicialização "ConfigureServices"
Na classe de inicialização, precisamos inicializar o perfil que criamos e também registrar o serviço AutoMapper.
Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
services.AddAutoMapper();
Fragmento de código para mostrar o método ConfigureServices onde precisamos inicializar e registrar o 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);
}}
Etapa 5. Obter saída.
Para obter o resultado mapeado, precisamos chamar AutoMapper.Mapper.Map e passar o destino e a origem adequados.
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);
}
}
'Mapper' does not contain a definition for 'initialize'
. Estou usando a AutoMapper.Extensions.Microsoft.DependencyInjection
versão 7.0.0
Quero estender as respostas de @ theutz - ou seja, esta linha:
// services.AddAutoMapper(typeof(Startup)); // <-- newer automapper version uses this signature.
Há um erro ( provavelmente ) no AutoMapper.Extensions.Microsoft.DependencyInjection versão 3.2.0. (Estou usando o .NET Core 2.0)
Isso é abordado nesta edição do GitHub. Se suas classes que herdam a classe Profile do AutoMapper existirem fora da montagem onde você está na classe Startup, elas provavelmente não serão registradas se a injeção do AutoMapper estiver assim:
services.AddAutoMapper();
a menos que você especifique explicitamente quais assemblies procurar pelos perfis do AutoMapper.
Isso pode ser feito assim em sua Startup.ConfigureServices:
services.AddAutoMapper(<assembies> or <type_in_assemblies>);
onde "assemblies" e "type_in_assemblies" apontam para o assembly em que as classes Profile em seu aplicativo são especificadas. Por exemplo:
services.AddAutoMapper(typeof(ProfileInOtherAssembly), typeof(ProfileInYetAnotherAssembly));
Eu suponho que (e eu coloquei ênfase nessa palavra) que, devido a seguinte implementação de sobrecarga sem parâmetros (código-fonte do GitHub ):
public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
return services.AddAutoMapper(null, AppDomain.CurrentDomain.GetAssemblies());
}
contamos com o CLR que já montou o JIT, contendo perfis do AutoMapper, que podem ou não ser verdadeiros, pois são acionados apenas quando necessário (mais detalhes nesta pergunta do StackOverflow).
A resposta do theutz aqui é muito boa, só quero acrescentar:
Se você deixar o seu perfil de mapeamento herdar em MapperConfigurationExpression
vez de Profile
, poderá simplesmente adicionar um teste para verificar sua configuração de mapeamento, o que é sempre ú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();
}
Resolvi-o desta maneira (semelhante ao acima, mas sinto que é uma solução mais limpa) para o .NET Core 2.2 / Automapper 8.1.1 com Extensions.DI 6.1.1.
Crie a classe MappingProfile.cs e preencha o construtor com o Maps (pretendo usar uma única classe para armazenar todos os meus mapeamentos)
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Source, Dest>().ReverseMap();
}
}
Em Startup.cs, adicione abaixo para adicionar ao DI (o argumento arg é para a classe que contém suas configurações de mapeamento, no meu caso, é a classe MappingProfile).
//add automapper DI
services.AddAutoMapper(typeof(MappingProfile));
No Controller, use-o como faria com qualquer outro 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);
}
}
MappingProfiles
-se new Type[]{}
como mostrado nesta resposta .
No meu Startup.cs (Core 2.2, Automapper 8.1.1)
services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });
No meu projeto de acesso a dados
namespace DAL
{
public class MapperProfile : Profile
{
// place holder for AddAutoMapper (to bring in the DAL assembly)
}
}
Na minha definição de modelo
namespace DAL.Models
{
public class PositionProfile : Profile
{
public PositionProfile()
{
CreateMap<Position, PositionDto_v1>();
}
}
public class Position
{
...
}
services.AddAutoMapper( typeof(DAL.MapperProfile) );
não usa services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });
?
Eu gosto de muitas respostas, principalmente a de @saineshwar. Estou usando o .net Core 3.0 com o AutoMapper 9.0, então acho que é hora de atualizar sua resposta.
O que funcionou para mim foi no Startup.ConfigureServices (...) registrar o serviço da seguinte maneira:
services.AddAutoMapper(cfg => cfg.AddProfile<MappingProfile>(),
AppDomain.CurrentDomain.GetAssemblies());
Eu acho que o resto da resposta @saineshwar continua perfeito. Mas se alguém estiver interessado, meu código de controlador é:
[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);
}
E minha classe de mapeamento:
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();
}
}
----- EDIT -----
Depois de ler os documentos vinculados nos comentários de Lucian Bargaoanu, acho melhor mudar um pouco essa resposta.
O parâmetro services.AddAutoMapper()
(que teve a resposta @saineshwar) não funciona mais (pelo menos para mim). Mas se você usar o assembly NuGet AutoMapper.Extensions.Microsoft.DependencyInjection, a estrutura poderá inspecionar todas as classes que estendem o AutoMapper.Profile (como o meu, MappingProfile).
Portanto, no meu caso, onde a classe pertence ao mesmo assembly em execução, o registro do serviço pode ser reduzido para services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(Uma abordagem mais elegante pode ser uma extensão sem parâmetros com essa codificação).
Obrigado, Lucian!
Estou usando o AutoMapper 6.1.1 e o asp.net Core 1.1.2.
Antes de tudo, defina as classes Profile herdadas pela Profile Class of Automapper. Criei a interface IProfile vazia, o objetivo é apenas encontrar as classes desse tipo.
public class UserProfile : Profile, IProfile
{
public UserProfile()
{
CreateMap<User, UserModel>();
CreateMap<UserModel, User>();
}
}
Agora crie uma classe separada, por exemplo, Mapeamentos
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
});
}
}
}
}
Agora no MVC Core web Project no arquivo Startup.cs, no construtor, chame a classe Mapping que inicializará todos os mapeamentos no momento do carregamento do aplicativo.
Mappings.RegisterMappings();
Para o ASP.NET Core (testado usando 2.0+ e 3.0), se você preferir ler a documentação de origem: https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md
Caso contrário, siga estes 4 passos:
Instale o AutoMapper.Extensions.Microsoft.DependancyInjection a partir do nuget.
Basta adicionar algumas classes de perfil.
Em seguida, adicione abaixo à sua classe startup.cs.
services.AddAutoMapper(OneOfYourProfileClassNamesHere)
Em seguida, basta injetar o IMapper em seus controladores ou onde você precisar:
public class EmployeesController {
private readonly IMapper _mapper;
public EmployeesController(IMapper mapper){
_mapper = mapper;
}
E se você quiser usar o ProjectTo agora, basta:
var customers = await dbContext.Customers.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider).ToListAsync()
Para o 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)
}
}
Na sua inicialização:
services.AddAutoMapper(GetAutoMapperProfilesFromAllAssemblies()
.ToArray());
No controlador ou serviço: injetar mapeador:
private readonly IMapper _mapper;
Uso:
var obj = _mapper.Map<TDest>(sourceObject);
Para adicionar o que Arve Systad mencionou para teste. Se por algum motivo você for como eu e quiser manter a estrutura de herança fornecida na solução theutz, poderá configurar a Configuração do Mapper da seguinte maneira:
var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(mappingProfile);
});
var mapper = new Mapper(config);
Eu fiz isso no NUnit.
services.AddAutoMapper (); não funcionou para mim. (Estou usando o Asp.Net Core 2.0)
Depois de configurar como abaixo
var config = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.CreateMap<ClientCustomer, Models.Customer>();
});
inicialize o mapeador IMapper mapper = config.CreateMapper ();
e adicione o objeto mapeador aos serviços como serviços singleton.AddSingleton (mapper);
Desta forma, eu sou capaz de adicionar um DI ao controlador
private IMapper autoMapper = null;
public VerifyController(IMapper mapper)
{
autoMapper = mapper;
}
e eu usei como abaixo nos meus métodos de ação
ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);
sobre a resposta theutz, não há necessidade de especificar o parâmetro do mapeador IMapper no construtor de controladores.
você pode usar o mapeador, pois é um membro estático em qualquer lugar do código.
public class UserController : Controller {
public someMethod()
{
Mapper.Map<User, UserDto>(user);
}
}
IMapper
você pode zombar disso e, por exemplo, fazer com que ele retorne nulo se for irrelevante para o teste fornecido.