Как настроить Automapper в ASP.NET Core

Я относительно новичок в.NET, и я решил заняться.NET Core вместо изучения "старых способов". Я нашел подробную статью о настройке AutoMapper для.NET Core здесь, но есть ли более простой способ для новичка?

20 ответов

Решение

Я понял! Вот подробности:

  1. Добавьте основной пакет AutoMapper в ваше решение через NuGet.
  2. Добавьте пакет внедрения зависимостей AutoMapper в ваше решение с помощью NuGet.

  3. Создайте новый класс для профиля сопоставления. (Я сделал класс в главном каталоге решений под названием 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>();
        }
    }
    
  4. Затем добавьте 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();
    
    }
    
  5. Чтобы вызвать сопоставленный объект в коде, сделайте что-то вроде следующего:

    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'.

Смотрите этот вопрос:

System.DateTime в «T MaxInteger[T](System.Collections.Generic.IEnumerable`1[T])» нарушает ограничение типа T для .NET 7 с использованием AutoMapper 11.0.1

Это означало, что я больше не мог использовать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);
   }
}
Другие вопросы по тегам