Cara mengatur Automapper di ASP.NET Core

255

Saya relatif baru di .NET, dan saya memutuskan untuk menangani .NET Core daripada belajar "cara lama". Saya menemukan artikel terperinci tentang pengaturan AutoMapper untuk .NET Core di sini , tetapi apakah ada langkah-langkah yang lebih sederhana untuk seorang pemula?

theutz
sumber
5
Lihat dotnetcoretutorials.com/2017/09/23/…
Michael Freidgeim
Untuk versi inti yang lebih baru (> v1) periksa jawaban @ Saineshwar stackoverflow.com/a/53455699/833878
Robbie
1
Jawaban lengkap dengan contoh klik tautan ini
Iman Bahrampour

Jawaban:

554

Saya menemukan jawabannya! Berikut detailnya:

  1. Tambahkan Paket AutoMapper utama ke solusi Anda melalui NuGet .
  2. Tambahkan Paket Injeksi Ketergantungan AutoMapper ke solusi Anda melalui NuGet .

  3. Buat kelas baru untuk profil pemetaan. (Saya membuat kelas di direktori solusi utama yang disebut MappingProfile.csdan menambahkan kode berikut.) Saya akan menggunakan objek Userdan UserDtosebagai contoh.

    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. Kemudian tambahkan AutoMapperConfiguration di Startup.csseperti yang ditunjukkan di bawah ini:

    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. Untuk memohon objek yang dipetakan dalam kode, lakukan sesuatu seperti berikut ini:

    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!
        }
    }

Saya harap ini membantu seseorang memulai dengan ASP.NET Core! Saya menyambut setiap umpan balik atau kritik karena saya masih baru di dunia .NET!

theutz
sumber
3
Artikel terperinci tertaut, lostechies.com/jimmybogard/2016/07/20/… , menjelaskan bagaimana Profilekelas berada
Kieren Johnstone
22
@theutz Anda dapat menggabungkan kedua garis CreateMap tersebut dengan .ReverseMap () di akhir, yah, baik. Mungkin berkomentar, tetapi saya merasa lebih intuitif.
Astravagrant
6
Mungkin bermanfaat pada Langkah 3 untuk menyebutkan menambahkan "menggunakan AutoMapper;" di bagian atas sehingga metode ekstensi diimpor.
Rocklan
8
Ini berfungsi baik dengan .net core 1.1, tidak lagi setelah saya memutakhirkan ke .net core 2.0. Saya pikir, saya perlu secara eksplisit menentukan perakitan kelas profil logika. Masih meneliti bagaimana mencapainya. Pembaruan: Ah jawabannya ada pada komentar Anda, saya harus lulus kelas typeof yang merupakan profil saya. // services.AddAutoMapper (typeof (Startup)); // <- versi automapper yang lebih baru menggunakan tanda tangan ini
Esen
3
Di AutoMapper v8 dan Dependency Injection v5 menambahkan, satu-satunya hal yang diperlukan adalah layanan.AddAutoMapper (); baris dalam metode ConfigureServices dari kelas Startup. Bagi saya, itu bahkan dapat menemukan kelas Profile di proyek perpustakaan kelas dependen.
stricq
69

Langkah Untuk Menggunakan AutoMapper dengan ASP.NET Core.

Langkah 1. Menginstal AutoMapper.Extensions.Microsoft.DependencyInjection dari Paket NuGet.

masukkan deskripsi gambar di sini

Langkah 2. Buat Folder di Solusi untuk menjaga Pemetaan dengan Nama "Pemetaan".

masukkan deskripsi gambar di sini

Langkah 3. Setelah menambahkan folder Pemetaan kami telah menambahkan kelas dengan Nama " MappingProfile " nama ini dapat menjadi sesuatu yang unik dan baik untuk dipahami.

Di kelas ini, kita akan Mempertahankan semua Pemetaan.

masukkan deskripsi gambar di sini

Langkah 4. Menginisialisasi Mapper di Startup "ConfigureServices"

Di Kelas Startup, kita Perlu Menginisialisasi Profil yang telah kita buat dan juga Mendaftar Layanan AutoMapper.

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

  services.AddAutoMapper();

Cuplikan Kode untuk memperlihatkan Metode ConfigureServices di mana kita perlu Menginisialisasi dan Mendaftar 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);

    }}

Langkah 5. Dapatkan Output.

Untuk mendapatkan hasil yang dipetakan kita perlu memanggil AutoMapper.Mapper.Map dan lulus Tujuan dan Sumber yang tepat.

AutoMapper.Mapper.Map<Destination>(source);

CodeSnippet

    [HttpPost]
    public void Post([FromBody] SchemeMasterViewModel schemeMaster)
    {
        if (ModelState.IsValid)
        {
            var mappedresult = AutoMapper.Mapper.Map<SchemeMaster>(schemeMaster);
        }
    }
Saineshwar
sumber
13
Saya mendapatkan error berikut: 'Mapper' does not contain a definition for 'initialize'. Saya menggunakan AutoMapper.Extensions.Microsoft.DependencyInjectionversi 7.0.0
kimbaudi
Jawaban super rinci. Terima kasih Pak.
Rod Hartzell
1
jika Anda menggunakan ASP.NET CORE 3.0 periksa tutorial ini Cara Mengatur AutoMapper di ASP.NET Core 3.0 tutexchange.com/how-to-set-up-up-automapper-in-asp-net-core-3-0
Saineshwar
44

Saya ingin memperluas jawaban @ theutz - yaitu baris ini:

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

Ada bug ( mungkin ) di AutoMapper.Extensions.Microsoft.DependencyInjection versi 3.2.0. (Saya menggunakan .NET Core 2.0)

Ini ditangani dalam masalah GitHub ini . Jika kelas Anda mewarisi kelas Profil AutoMapper ada di luar perakitan di mana Anda kelas Startup adalah mereka mungkin tidak akan terdaftar jika injeksi AutoMapper Anda terlihat seperti ini:

services.AddAutoMapper();

kecuali jika Anda secara eksplisit menentukan majelis mana yang akan dicari profil AutoMapper.

Ini dapat dilakukan seperti ini di Startup Anda. Konfigurasi Layanan:

services.AddAutoMapper(<assembies> or <type_in_assemblies>);

di mana "assemblies" dan "type_in_assemblies" arahkan ke majelis tempat kelas Profile di aplikasi Anda ditentukan. Misalnya:

services.AddAutoMapper(typeof(ProfileInOtherAssembly), typeof(ProfileInYetAnotherAssembly));

Saya kira (dan saya menekankan kata ini) bahwa karena mengikuti penerapan parameterless overload (kode sumber dari GitHub ):

public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
     return services.AddAutoMapper(null, AppDomain.CurrentDomain.GetAssemblies());
}

kami mengandalkan CLR yang memiliki perakitan JITed yang berisi profil AutoMapper yang mungkin atau mungkin tidak benar karena hanya dipasangkan ketika dibutuhkan (lebih detail dalam pertanyaan StackOverflow ini ).

GrayCat
sumber
5
Itulah jawaban yang benar untuk versi terbaru AutoMapper dan AspNetCore
Joshit
1
ini adalah jawaban yang saya cari untuk AutoMapper 8.1 (versi terbaru)
Tinaira
30

jawaban theutz di sini sangat bagus, saya hanya ingin menambahkan ini:

Jika Anda membiarkan profil pemetaan Anda mewarisi MapperConfigurationExpressionalih-alih Profile, Anda dapat dengan mudah menambahkan tes untuk memverifikasi pengaturan pemetaan Anda, yang selalu berguna:

[Fact]
public void MappingProfile_VerifyMappings()
{
    var mappingProfile = new MappingProfile();

    var config = new MapperConfiguration(mappingProfile);
    var mapper = new Mapper(config);

    (mapper as IMapper).ConfigurationProvider.AssertConfigurationIsValid();
}
Arve Systad
sumber
Saya mendapatkan satu kesalahan: "Injeksi Ketergantungan Ekstensi AutoMapper tidak kompatibel dengan asp.net core 1.1". Tolong bantu!
Rohit Arora
Tampaknya definisi "verifikasi" untuk diperdebatkan. Ini meledak ketika properti tertentu dinonaktifkan oleh desain untuk mencegah pemetaan.
Jeremy Holovacs
2
Jika Anda tidak ingin properti dipetakan, atur dengan .Ignore (). Dengan begitu, itu memaksa Anda untuk secara aktif berpikir untuk menangani setiap kasus - memastikan Anda tidak ketinggalan barang ketika ada perubahan. Sebenarnya super praktis. Jadi ya, uji verifikasi adalah jaring pengaman yang lebih besar daripada yang disadari banyak orang. Ini tidak mudah, tetapi itu mengurus 90% pertama.
Arve Systad
18

Saya memecahkannya dengan cara ini (mirip dengan di atas tetapi saya merasa itu adalah solusi yang lebih bersih) untuk .NET Core 2.2 / Automapper 8.1.1 w / Extensions.DI 6.1.1.

Buat kelas MappingProfile.cs dan isi konstruktor dengan Maps (Saya berencana menggunakan satu kelas untuk menampung semua pemetaan saya)

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

Di Startup.cs, tambahkan di bawah ini untuk menambahkan ke DI (argumen assembly adalah untuk kelas yang menyimpan konfigurasi pemetaan Anda, dalam kasus saya, ini adalah kelas MappingProfile).

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

Di Kontroler, gunakan seperti yang Anda lakukan pada objek DI lainnya

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

Coy Meeks
sumber
2
Saya suka jawaban Anda. Saya pikir membungkus MappingProfilesdengan new Type[]{}seperti yang ditunjukkan dalam jawaban ini tidak perlu.
Manusia Terlalu Gemuk Tanpa Leher
10

Di Startup.cs saya (Core 2.2, Automapper 8.1.1)

services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });            

Dalam proyek akses data saya

namespace DAL
{
    public class MapperProfile : Profile
    {
        // place holder for AddAutoMapper (to bring in the DAL assembly)
    }
}

Dalam definisi model saya

namespace DAL.Models
{
    public class PositionProfile : Profile
    {
        public PositionProfile()
        {
            CreateMap<Position, PositionDto_v1>();
        }
    }

    public class Position
    {
        ...
    }
Brian Rice
sumber
Mengapa tidak Anda gunakan services.AddAutoMapper( typeof(DAL.MapperProfile) ); saja services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });?
Manusia Terlalu Gemuk Tanpa Leher
8

Saya suka banyak jawaban, terutama yang @saineshwar. Saya menggunakan .net Core 3.0 dengan AutoMapper 9.0, jadi saya merasa sudah waktunya untuk memperbarui jawabannya.

Apa yang berhasil bagi saya adalah di Startup.ConfigureServices (...) daftarkan layanan dengan cara ini:

    services.AddAutoMapper(cfg => cfg.AddProfile<MappingProfile>(), 
                               AppDomain.CurrentDomain.GetAssemblies());

Saya pikir sisa jawaban @saineshwar tetap sempurna. Tetapi jika ada yang tertarik kode controller saya adalah:

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

Dan kelas pemetaan saya:

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

Setelah membaca dokumen yang terhubung dalam komentar oleh Lucian Bargaoanu, saya pikir lebih baik untuk mengubah jawaban ini sedikit.

Tanpa parameter services.AddAutoMapper() (yang memiliki jawaban @saineshwar) tidak berfungsi lagi (setidaknya untuk saya). Tetapi jika Anda menggunakan perakitan NuGet AutoMapper.Extensions.Microsoft.DependencyInjection, kerangka kerja ini dapat memeriksa semua kelas yang memperpanjang AutoMapper.Profile (seperti tambang, MappingProfile).

Jadi, dalam kasus saya, di mana kelas milik majelis pelaksana yang sama, pendaftaran layanan dapat dipersingkat menjadi services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(Pendekatan yang lebih elegan bisa menjadi ekstensi tanpa parameter dengan pengkodean ini).

Terima kasih, Lucian!

Vic
sumber
6

Saya menggunakan AutoMapper 6.1.1 dan asp.net Core 1.1.2.

Pertama-tama, tentukan kelas Profil yang diwarisi oleh Kelas Profil Automapper. Saya Membuat antarmuka IProfile yang kosong, tujuannya hanya untuk menemukan kelas-kelas jenis ini.

 public class UserProfile : Profile, IProfile
    {
        public UserProfile()
        {
            CreateMap<User, UserModel>();
            CreateMap<UserModel, User>();
        }
    }

Sekarang buat kelas yang terpisah misalnya Pemetaan

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

    }

Sekarang di Proyek web MVC Core di file Startup.cs, di konstruktor, panggil kelas Pemetaan yang akan menginisialisasi semua pemetaan pada saat memuat aplikasi.

Mappings.RegisterMappings();
Aamir
sumber
Anda cukup membuat subkelas dari kelas profil, dan saat program menjalankan layanan.AddAutoMapper (); baris kode Automapper secara otomatis mengetahuinya.
isaeid
Saya tidak berpikir ini perlu jika Anda menggunakan AutoMapper.Extensions.Microsoft.DependancyInjection yang tersedia di nuget.
Greg Gum
5

Untuk ASP.NET Core (diuji menggunakan 2.0+ dan 3.0), jika Anda lebih suka membaca dokumentasi sumber: https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md

Jika tidak, ikuti 4 langkah ini berfungsi:

  1. Instal AutoMapper.Extensions.Microsoft.DependancyInjection dari nuget.

  2. Cukup tambahkan beberapa kelas profil.

  3. Kemudian tambahkan di bawah ini ke kelas startup.cs Anda. services.AddAutoMapper(OneOfYourProfileClassNamesHere)

  4. Kemudian cukup Suntikkan IMapper di pengontrol Anda atau di mana pun Anda membutuhkannya:

public class EmployeesController {

    private readonly IMapper _mapper;

    public EmployeesController(IMapper mapper){

        _mapper = mapper;
    }

Dan jika Anda ingin menggunakan ProjectTo sekarang cukup:

var customers = await dbContext.Customers.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider).ToListAsync()
Dalcam
sumber
4

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

Profilperpile:

public class OrganizationProfile : Profile
{
  public OrganizationProfile()
  {
    CreateMap<Foo, FooDto>();
    // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
  }
}

Di Startup Anda:

services.AddAutoMapper(GetAutoMapperProfilesFromAllAssemblies()
            .ToArray());

Dalam Pengontrol atau layanan: Suntikkan mapper:

private readonly IMapper _mapper;

Pemakaian:

var obj = _mapper.Map<TDest>(sourceObject);
Nicolae Lupei
sumber
4

Pada versi terbaru dari core asp.net Anda harus menggunakan inisialisasi berikut:

services.AddAutoMapper(typeof(YourMappingProfileClass));
martcs
sumber
2

Asp.Net Core 2.2 dengan AutoMapper.Extensions.Microsoft.DependencyInjection.

public class MappingProfile : Profile
{
  public MappingProfile()
  {
      CreateMap<Domain, DomainDto>();
  }
}

Di Startup.cs

services.AddAutoMapper(typeof(List.Handler));
Sras
sumber
1

Untuk menambah apa yang disebutkan Arve Systad untuk pengujian. Jika karena alasan apa pun Anda seperti saya dan ingin mempertahankan struktur warisan yang disediakan dalam solusi theutz, Anda dapat mengatur MapperConfiguration seperti:

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

Saya melakukan ini di NUnit.

LandShark
sumber
1

services.AddAutoMapper (); tidak bekerja untuk saya. (Saya menggunakan Asp.Net Core 2.0)

Setelah mengkonfigurasi seperti di bawah ini

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

inisialisasi mapper IMapper mapper = config.CreateMapper ();

dan menambahkan objek mapper ke layanan sebagai layanan singleton. Tambahkan Adingleton (mapper);

dengan cara ini saya dapat menambahkan DI ke controller

  private IMapper autoMapper = null;

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

dan saya telah menggunakan seperti di bawah ini dalam metode tindakan saya

  ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);
Venkat pv
sumber
Hai @venkat Anda mungkin hanya perlu menambahkan paket AutoMapper.Extensions.Microsoft.DependancyInjection ke proyek Anda
dalcam
-1

tentang theutz jawaban, tidak perlu menentukan parrameter mapper IMapper pada konstruktor controller.

Anda dapat menggunakan Mapper karena ia adalah anggota statis di sembarang tempat kode.

public class UserController : Controller {
   public someMethod()
   {
      Mapper.Map<User, UserDto>(user);
   }
}
yaronmil
sumber
11
Tapi statika agak anti-diuji, bukan?
Scott Fraley
3
Ya. Ini akan berfungsi dalam banyak kasus, tetapi jika Anda tidak memiliki pemetaan yang dikonfigurasikan saat menjalankan metode ini dalam pengujian, Ini akan menimbulkan pengecualian (dan dengan demikian gagal dalam pengujian karena alasan yang salah). Dengan suntikan, IMapperAnda dapat mengejek itu dan, misalnya, hanya mengembalikannya nol jika tidak relevan untuk tes yang diberikan.
Arve Systad