Як налаштувати Automapper в ASP.NET Core


254

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



Для більш нових версій ядра (> v1) перевірити @ відповідь Saineshwar в stackoverflow.com/a/53455699/833878
— Роббі

1
Повна відповідь із прикладом перейдіть за цим посиланням
— Іман Бахрампур

Відповіді:


553

Я зрозумів це! Ось деталі:

  1. Додайте основний пакет автомашини до свого рішення через 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!


3
Докладна стаття, що пов’язана, lostechies.com/jimmybogard/2016/07/20/… , пояснює, як Profileрозташовані заняття
— Кірен Джонстон

22
@theutz Ви можете об'єднати ці дві лінії CreateMap з .ReverseMap () наприкінці, ну, будь-яким. Можливо, прокоментуйте це, але я вважаю це більш інтуїтивно зрозумілим.
— Астравагрант

6
На кроці 3 може бути корисним згадати про додавання "за допомогою AutoMapper;" вгорі, щоб імпортувати метод розширення.
— Rocklan

8
Це добре працювало з .net core 1.1, не раз я перейшов на .net core 2.0. Я думаю, мені потрібно чітко вказати складання класу логічного профілю. Досі вивчаємо, як цього досягти. Оновлення: А відповідь міститься у вашому коментарі, я повинен пройти клас typeof, який є моїм профілем. // services.AddAutoMapper (typeof (Startup)); // <- новіша версія автоматичної версії використовує цей підпис
— Esen

3
У додатку AutoMapper v8 та доповненні Dependency Injection v5 єдине, що потрібно - це служби.AddAutoMapper (); рядок у методі ConfigureServices класу Startup. Для мене навіть вдалося знайти класи профілю у бібліотечних проектах залежних класів.
— stricq

68

Крок, щоб використовувати AutoMapper з ASP.NET Core.

Крок 1. Встановлення AutoMapper.Extensions.Microsoft.DependencyInjection з NuGet Package.

введіть тут опис зображення

Крок 2. Створіть папку в розчині, щоб зберегти відображення з назвою "Відображення".

введіть тут опис зображення

Крок 3. Після додавання папки Mapping ми додали клас з ім'ям " MappingProfile ", це ім'я може зробити що-небудь унікальне і добре зрозуміти.

У цьому класі ми збираємося підтримувати всі відображення.

введіть тут опис зображення

Крок 4. Ініціалізація Mapper у запуску "ConfigureServices"

У класі запуску нам потрібно ініціалізувати створений нами профіль, а також зареєструвати сервіс автозапчастини.

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

  services.AddAutoMapper();

Фрагмент коду, щоб показати метод ConfigureServices, де нам потрібно ініціалізувати та зареєструвати автомат.

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);
        }
    }

13
Я отримую наступне повідомлення про помилку: 'Mapper' does not contain a definition for 'initialize'. Я використовую AutoMapper.Extensions.Microsoft.DependencyInjectionверсію 7.0.0
— kimbaudi

Супер детальна відповідь. Дякую вам сер.
— Rod

1
якщо ви використовуєте ASP.NET CORE 3.0, ознайомтеся з цим підручником Як налаштувати AutoMapper в ASP.NET Core 3.0 tutexchange.com/how-to-set-up-automapper-in-asp-net-core-3-0
— Saineshwar

44

Я хочу поширити відповіді @ theutz - а саме цей рядок:

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

У AutoMapper.Extensions.Microsoft.DependencyInjection версії 3.2.0 є помилка ( ймовірно ). (Я використовую .NET Core 2.0)

Про це йдеться у цьому випуску GitHub. Якщо ваші класи, що успадковують профіль профілю AutoMapper, існують поза збіркою, де ви запускаєте клас, вони, ймовірно, не будуть зареєстровані, якщо ваша інжекція 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, що вже має збірку JITed, що містить профілі AutoMapper, які можуть бути або можуть бути неправдивими, оскільки вони можуть бути зіткнутими лише у разі потреби (детальніше у цьому питанні StackOverflow).


5
Точна відповідь на останню версію AutoMapper та AspNetCore
— Joshit

1
цю відповідь я шукав у AutoMapper 8.1 (остання версія)
— Тінайра

30

Відповідь 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();
}

Я отримую одну помилку: "Введення залежності залежності розширення AutoMapper не сумісне з ядром asp.net 1.1". Будь ласка, допоможіть!
— Рохіт Арора

Здається, визначення "перевірити" підходить для обговорення. Це вибухає, коли певні властивості дизайн опущено, щоб не допустити відображення.
— Джеремі Головач

2
Якщо ви не хочете, щоб властивість відображалося на карті, встановіть його за допомогою .Ignore (). Таким чином, воно змушує вас активно замислюватися над вирішенням кожного конкретного випадку - переконуючись, що ви не пропустите речі під час внесення змін. Насправді супер практично. Так, так, верифікаційний тест є більшою мережею безпеки, ніж багато хто розуміє. Це не дурний захист, але він піклується про перші 90%.
— Арве Систад

18

Я вирішив це таким чином (подібно до вище, але я відчуваю, що це чистіше рішення) для .NET Core 2.2 / Automapper 8.1.1 w / Extensions.DI 6.1.1.

Створіть клас MappingProfile.cs та заповніть конструктор за допомогою Maps (я планую використовувати один клас для зберігання всіх моїх відображень)

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

У Startup.cs додайте нижче, щоб додати до DI (аргумент збірки призначений для класу, який містить ваші конфігураційні карти, в моєму випадку це клас MappingProfile).

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

У Controller використовуйте його так, як і будь-який інший об'єкт 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
Мені подобається ваша відповідь. Я думаю , що упаковка MappingProfilesз , new Type[]{}як показано в цій відповіді не потрібно.
— Програміст, орієнтований на гроші,

10

У моєму 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
    {
        ...
    }

Чому ви просто не використовуєте services.AddAutoMapper( typeof(DAL.MapperProfile) ); замість цього services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });?
— Програміст, орієнтований на гроші,

8

Мені подобається багато відповідей, зокрема, @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();
    }
}

----- EDIT -----

Прочитавши документи, зв'язані в коментарях Лучана Баргаоану, я думаю, що краще трохи змінити цю відповідь.

Параметр services.AddAutoMapper()(на який був відповідь @saineshwar) більше не працює (принаймні для мене). Але якщо ви використовуєте збірку NuGet AutoMapper.Extensions.Microsoft.DependencyInjection, фреймворк може перевірити всі класи, що розширюють AutoMapper.Profile (як, наприклад, моя, MappingProfile).

Отже, у моєму випадку, коли клас належить до тієї ж виконувальної збірки, реєстрацію послуги можна скоротити до services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(Більш елегантним підходом може бути розширення без параметрів за допомогою цього кодування).

Спасибі, Лучане!



6

Я використовую AutoMapper 6.1.1 та asp.net Core 1.1.2.

Перш за все, визначте класи профілю, успадковані профілю Class Class 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();

Ви можете просто створити підклас з профілю профілю та коли програма запускає служби.AddAutoMapper (); рядок кодів Автоматпер їх автоматично знає.
— isaeid

Я не думаю, що це є необхідним, якщо ви використовуєте AutoMapper.Extensions.Microsoft.DependancyInjection, який доступний у цілому.
— Грег Гум

5

Для ASP.NET Core (тестується з використанням 2.0+ та 3.0), якщо ви бажаєте прочитати вихідну документацію: https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md

В іншому випадку виконання цих 4 кроків працює:

  1. Встановіть AutoMapper.Extensions.Microsoft.DependancyInjection з nuget.

  2. Просто додайте кілька профільних класів.

  3. Потім додайте нижче до класу startup.cs. services.AddAutoMapper(OneOfYourProfileClassNamesHere)

  4. Тоді просто введіть 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()

4

Для 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());

У контролері чи сервісі: Ін'єкційний картограф:

private readonly IMapper _mapper;

Використання:

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

4

В останніх версіях ядра asp.net слід використовувати таку ініціалізацію:

services.AddAutoMapper(typeof(YourMappingProfileClass));

2

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));

1

Щоб додати те, що Arve Systad згадав для тестування. Якщо з будь-якої причини ви схожі на мене і хочете зберегти структуру спадкування, надану в рішенні theutz, ви можете встановити MapperConfiguration так:

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

Я зробив це в NUnit.


1

services.AddAutoMapper (); не працювало для мене. (Я використовую Asp.Net Core 2.0)

Після налаштування, як показано нижче

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

ініціалізувати Mapper IMapper mapper = config.CreateMapper ();

і додайте об’єкт mapper до сервісів як однотонних служб.AddSingleton (mapper);

таким чином я можу додати DI до контролера

  private IMapper autoMapper = null;

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

і я використовував, як показано нижче, у своїх методах дії

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

Привіт @venkat вам, мабуть, просто потрібно було додати пакет AutoMapper.Extensions.Microsoft.DependancyInjection до вашого проекту
— dalcam

-1

щодо відповіді theutz, немає необхідності вказувати параметрметра Mapper IMapper у конструкторі контролерів.

ви можете використовувати Mapper, оскільки він є статичним членом у будь-якому місці коду.

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

11
Але статика трохи не перевірена, ні?
— Скотт Фрейлі

3
Так. Це буде спрацьовувати у багатьох випадках, але якщо у вас немає налаштованого відображення під час виклику цього методу в тесті, він викине виняток (і, таким чином, вийде з ладу тесту з неправильної причини). За допомогою ін'єкції IMapperви можете знущатися над цим і, наприклад, просто змушувати повернути його до нуля, якщо це не має значення для даного тесту.
— Арв Систад
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.