Как настроить Automapper в ASP.NET Core
Я относительно новичок в.NET, и я решил заняться.NET Core вместо изучения "старых способов". Я нашел подробную статью о настройке AutoMapper для.NET Core здесь, но есть ли более простой способ для новичка?
20 ответов
Я понял! Вот подробности:
- Добавьте основной пакет AutoMapper в ваше решение через NuGet.
Добавьте пакет внедрения зависимостей AutoMapper в ваше решение с помощью NuGet.
Создайте новый класс для профиля сопоставления. (Я сделал класс в главном каталоге решений под названием
MappingProfile.cs
и добавьте следующий код.) Я буду использоватьUser
а такжеUserDto
Объект в качестве примера.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>(); } }
Затем добавьте AutoMapperConfiguration в
Startup.cs
как показано ниже: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(); }
Чтобы вызвать сопоставленный объект в коде, сделайте что-то вроде следующего:
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! } }
Я надеюсь, что это поможет кому-то начать с ASP.NET Core! Я приветствую любые отзывы или критику, так как я все еще новичок в мире.NET!
Шаг Использовать AutoMapper с ASP.NET Core.
Шаг 1. Установка AutoMapper.Extensions.Microsoft.DependencyInjection из пакета NuGet.
Шаг 2. Создайте папку в Solution, чтобы сохранить сопоставления с именем "сопоставления".
Шаг 3. После добавления папки Mapping мы добавили класс с именем "MappingProfile", это имя может быть уникальным и полезным для понимания.
В этом классе мы собираемся сохранить все отображения.
Шаг 4. Инициализация Mapper при запуске "ConfigureServices"
В классе запуска нам нужно инициализировать созданный нами профиль, а также зарегистрировать службу AutoMapper.
Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
services.AddAutoMapper();
Фрагмент кода, чтобы показать метод ConfigureServices, где нам нужно инициализировать и зарегистрировать 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);
}}
Шаг 5. Получите вывод.
Чтобы получить сопоставленный результат, нам нужно вызвать AutoMapper.Mapper.Map и передать правильный пункт назначения и источник.
AutoMapper.Mapper.Map<Destination>(source);
CodeSnippet
[HttpPost]
public void Post([FromBody] SchemeMasterViewModel schemeMaster)
{
if (ModelState.IsValid)
{
var mappedresult = AutoMapper.Mapper.Map<SchemeMaster>(schemeMaster);
}
}
Я хочу расширить ответы @theutz, а именно:
// services.AddAutoMapper(typeof(Startup)); // <-- newer automapper version uses this signature.
Существует ошибка (вероятно) в AutoMapper.Extensions.Microsoft.DependencyInjection версии 3.2.0. (Я использую.NET Core 2.0)
Это решается в этой проблеме GitHub. Если ваши классы, наследующие класс Profile AutoMapper, существуют вне сборки, где вы используете класс Startup, они, вероятно, не будут зарегистрированы, если ваша инъекция AutoMapper выглядит следующим образом:
services.AddAutoMapper();
если вы явно не укажете, какие сборки искать в профилях AutoMapper.
Это можно сделать так в вашем Startup.ConfigureServices:
services.AddAutoMapper(<assembies> or <type_in_assemblies>);
где "сборки" и "type_in_assemblies" указывают на сборку, где указаны классы профиля в вашем приложении. Например:
services.AddAutoMapper(typeof(ProfileInOtherAssembly), typeof(ProfileInYetAnotherAssembly));
Я полагаю (и подчеркиваю это слово), что из-за следующей реализации перегрузки без параметров (исходный код из GitHub):
public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
return services.AddAutoMapper(null, AppDomain.CurrentDomain.GetAssemblies());
}
мы полагаемся на то, что в CLR уже есть сборка JIT, содержащая профили AutoMapper, которые могут быть или не быть истинными, поскольку они соединяются только при необходимости (больше деталей в этом вопросе Stackru).
Я решил это таким образом (аналогично предыдущему, но мне кажется, что это более чистое решение) для.NET Core 2.2/Automapper 8.1.1 с Extensions.DI 6.1.1.
Создайте класс MappingProfile.cs и заполните конструктор картами (я планирую использовать один класс для хранения всех моих сопоставлений)
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Source, Dest>().ReverseMap();
}
}
В Startup.cs добавьте ниже, чтобы добавить в DI (аргумент сборки предназначен для класса, который содержит ваши конфигурации сопоставления, в моем случае это класс MappingProfile).
//add automapper DI
services.AddAutoMapper(typeof(MappingProfile));
В контроллере используйте его, как любой другой объект 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);
}
}
Ответ theutz здесь очень хороший, я просто хочу добавить это:
Если вы позволите вашему профилю сопоставления наследовать от MapperConfigurationExpression
вместо Profile
Вы можете очень просто добавить тест, чтобы проверить настройки отображения, что всегда удобно:
[Fact]
public void MappingProfile_VerifyMappings()
{
var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(mappingProfile);
var mapper = new Mapper(config);
(mapper as IMapper).ConfigurationProvider.AssertConfigurationIsValid();
}
Мне нравится много ответов, особенно @saineshwar. Я использую.net Core 3.0 с AutoMapper 9.0, поэтому считаю, что пора обновить его ответ.
Что сработало для меня, так это в Startup.ConfigureServices(...) зарегистрируйте службу следующим образом:
services.AddAutoMapper(cfg => cfg.AddProfile<MappingProfile>(),
AppDomain.CurrentDomain.GetAssemblies());
Я думаю, что остальная часть ответа @saineshwar остается идеальной. Но если кому-то интересно, мой код контроллера:
[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);
}
И мой класс отображения:
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();
}
}
----- РЕДАКТИРОВАТЬ -----
После прочтения документов, связанных в комментариях Лучиана Баргаоану, я думаю, что лучше немного изменить этот ответ.
Без параметров services.AddAutoMapper()
(у которого был ответ @saineshwar) больше не работает (по крайней мере, для меня). Но если вы используете сборку NuGet AutoMapper.Extensions.Microsoft.DependencyInjection, фреймворк сможет проверять все классы, расширяющие AutoMapper.Profile (например, мой MappingProfile).
Итак, в моем случае, когда класс принадлежит одной и той же исполняющей сборке, регистрацию службы можно сократить до services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(Более элегантным подходом могло бы быть расширение без параметров с этим кодированием).
Спасибо, Люциан!
В последних версиях ядра asp.net следует использовать следующую инициализацию:
services.AddAutoMapper(typeof(YourMappingProfileClass));
Необходимо установить пакет для настройки автомата.
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
После AddAutoMapper будет доступен в services.
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(typeof(Startup));
}
Создайте преобразователь из класса Employee в EmployeeDTO.
using AutoMapper;
public class AutomapperProfile: Profile
{
public AutomapperProfile()
{
//Source to destination.
CreateMap<Employee,EmployeeDTO>();
}
}
EmployeeController сопоставляет Employee с EmployeeDTo
using System.Collections.Generic;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
[Route("api/[controller]")]
[ApiController()]
public class EmployeeController : ControllerBase
{
private readonly IMapper _mapper;
public EmployeeController(IMapper mapper)
{
_mapper = mapper;
}
[HttpGet]
public IEnumerable<EmployeeDTO> GetEmployees()
{
/*
Assume it to be a service call/database call
it returns a list of employee, and now we will map it to EmployeeDTO
*/
var employees = Employee.SetupEmployee();
var employeeDTO = _mapper.Map<IEnumerable<EmployeeDTO>>(employees);
return employeeDTO;
}
}
Employee.cs для справки
using System.Collections.Generic;
public class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public int Salary { get; set; }
public static IEnumerable<Employee> SetupEmployee()
{
return new List<Employee>()
{
new Employee(){EmployeeId = 1, EmployeeName ="First", Salary=10000},
new Employee(){EmployeeId = 2, EmployeeName ="Second", Salary=20000},
new Employee(){EmployeeId = 3, EmployeeName ="Third", Salary=30000},
new Employee(){EmployeeId = 4, EmployeeName ="Fourth", Salary=40000},
new Employee(){EmployeeId = 5, EmployeeName ="Fifth", Salary=50000}
};
}
}
EmployeeDTO.cs для справки
public class EmployeeDTO
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
}
Для 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)
}
}
В вашем автозагрузке:
services.AddAutoMapper(GetAutoMapperProfilesFromAllAssemblies()
.ToArray());
В контроллере или сервисе: Inject mapper:
private readonly IMapper _mapper;
Применение:
var obj = _mapper.Map<TDest>(sourceObject);
В моем Startup.cs (Core 2.2, Automapper 8.1.1)
services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });
В моем проекте доступа к данным
namespace DAL
{
public class MapperProfile : Profile
{
// place holder for AddAutoMapper (to bring in the DAL assembly)
}
}
В моем определении модели
namespace DAL.Models
{
public class PositionProfile : Profile
{
public PositionProfile()
{
CreateMap<Position, PositionDto_v1>();
}
}
public class Position
{
...
}
Я использую AutoMapper 6.1.1 и asp.net Core 1.1.2.
Прежде всего, определите классы профиля, унаследованные классом профиля Automapper. Я создал интерфейс IProfile, который пуст, цель состоит только в том, чтобы найти классы этого типа.
public class UserProfile : Profile, IProfile
{
public UserProfile()
{
CreateMap<User, UserModel>();
CreateMap<UserModel, User>();
}
}
Теперь создайте отдельный класс, например, Mappings
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
});
}
}
}
}
Теперь в веб-проекте MVC Core в файле Startup.cs в конструкторе вызовите класс Mapping, который будет инициализировать все сопоставления во время загрузки приложения.
Mappings.RegisterMappings();
В .NET 6 вам нужно добавить в файл Program.cs следующее:
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
Для ASP.NET Core следующее является прямым от Automapper и занимает 1 строку в вашем классе запуска: https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md
Просто добавьте несколько профильных классов. Затем добавьте ниже в свой класс startup.cs.services.AddAutoMapper(OneOfYourProfileClassNamesHereSoItCanFindYourProfileAssembly)
Затем просто вставьте IMapper в свои контроллеры или куда вам нужно:
public class EmployeesController {
private readonly IMapper _mapper;
public EmployeesController(IMapper mapper){
_mapper = mapper;
}
И если вы хотите использовать ProjectTo сейчас, просто:
var customers = await dbContext.Customers.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider).ToListAsync()
Давайте посмотрим, как добавить Auto mapper в наше приложение .NET Core.
step: 1 Первым шагом является установка соответствующего пакета NuGet:
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
шаг 2
После установки необходимого пакета следующим шагом будет настройка служб. Сделаем это в классе Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(typeof(Startup));
services.AddControllersWithViews();
}
шаг 3
Приступим к использованию, у нас есть объект домена с именем User:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Address { get; set; }
}
На уровне пользовательского интерфейса у нас будет модель представления для отображения информации о пользователе:
public class UserViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
шаг: 4
Хороший способ организовать наши конфигурации сопоставления - использовать профили. Нам нужно создать классы, которые наследуются от класса Profile, и поместить конфигурацию в конструктор:
public UserProfile()
{
CreateMap<User, UserViewModel>();
}
шаг: 5
Теперь давайте определим контроллер и воспользуемся только что добавленными возможностями автоматического сопоставления:
public class UserController : Controller
{
private readonly IMapper _mapper;
public UserController(IMapper mapper)
{
_mapper = mapper;
}
public IActionResult Index()
{
// Populate the user details from DB
var user = GetUserDetails();
UserViewModel userViewModel = _mapper.Map<UserViewModel>(user);
return View(userViewModel);
}
}
Сначала мы вводим объект сопоставления в контроллер. Затем мы вызываем метод Map (), который сопоставляет объект User с объектом UserViewModel. Кроме того, обратите внимание на локальный метод GetUserDetails, который мы используем для локального хранения данных. Вы можете найти его реализацию в нашем исходном коде.
Первый шаг — установить соответствующий пакет nuget для AutoMapper.
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
После установки пакета нам необходимо настроить файл program.cs.
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddControllersWithViews()
Вот мой класс клиента и getCustomerDto
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class getCustomerDto
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Мы можем создать новый файл «AutoMapperProfiles.cs» для всех конфигураций automapper. В этом файле нам нужно создать класс, который наследует профили. А в Конструкторе мы можем иметь настройки автосопоставления.
public class AutoMapper: Profile {
public AutoMapper()
{
CreateMap < Customer, getCustomerDto > ();
}
}
В нашем контроллере мы можем использовать определенную нами конфигурацию автоматического картографа.
public class CustomerController : Controller
{
private readonly IMapper _mapper;
public CustomerController(IMapper mapper)
{
_mapper = mapper;
}
public IActionResult Index()
{
var customer = GetCustomerDetails();
var customerDetails = _mapper.Map<getCustomerDto>(customer);
return View(customerDetails);
}
}
Теперь, делая это, свойства класса Customer автоматически сопоставляются с customerDetailsDto.
services.AddAutoMapper(); не работал для меня (Я использую Asp.Net Core 2.0)
После настройки, как показано ниже
var config = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.CreateMap<ClientCustomer, Models.Customer>();
});
инициализировать маппер IMapper mapper = config.CreateMapper();
и добавьте объект mapper к сервисам как singleton services.AddSingleton(mapper);
таким образом, я могу добавить DI в контроллер
private IMapper autoMapper = null;
public VerifyController(IMapper mapper)
{
autoMapper = mapper;
}
и я использовал, как показано ниже, в моих методах действий
ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);
Asp.Net Core 2.2 с AutoMapper.Extensions.Microsoft.DependencyInjection.
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Domain, DomainDto>();
}
}
В Startup.cs
services.AddAutoMapper(typeof(List.Handler));
ДляAutoMapper 11.0.1
с использованием.NET 7
Я начал получать это исключение:
System.ArgumentException: 'GenericArguments[0], 'System.DateTime', on 'T MaxInteger[T](System.Collections.Generic.IEnumerable`1[T])' violates the constraint of type 'T'.'
Inner Exception
VerificationException: Method System.Linq.Enumerable.MaxInteger: type argument 'System.DateTime' violates the constraint of type parameter 'T'.
Смотрите этот вопрос:
Это означало, что я больше не мог использоватьservices.AddAutoMapper(typeof(MappingProfile).Assembly);
без исключения.
ДляAutoMapper.Extensions.Microsoft.DependencyInjection
Я решил это так:
services.AddAutoMapper(cfg => cfg.Internal().MethodMappingEnabled = false, typeof(MappingProfile).Assembly);
Для клиента Blazor WebAssembly решение выглядело так:
var mapperConfig = new MapperConfiguration(mc =>
{
//Needed for https://github.com/AutoMapper/AutoMapper/issues/3988
mc.Internal().MethodMappingEnabled = false;
mc.AddProfile(new MappingProfile());
});
//mapperConfig.AssertConfigurationIsValid();
IMapper mapper = mapperConfig.CreateMapper();
builder.Services.AddSingleton(mapper);
Чтобы добавить то, что Арве Систад упомянул для тестирования. Если по какой-либо причине вы похожи на меня и хотите сохранить структуру наследования, представленную в решении theutz, вы можете настроить MapperConfiguration следующим образом:
var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(mappingProfile);
});
var mapper = new Mapper(config);
Я сделал это в NUnit.
Что касается ответа theutz, то нет необходимости указывать параметр IMapper mapper в конструкторе контроллеров.
Вы можете использовать Mapper, поскольку он является статическим элементом в любом месте кода.
public class UserController : Controller {
public someMethod()
{
Mapper.Map<User, UserDto>(user);
}
}