Як налаштувати 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.