Ketika kita membuat sebuah aplikasi dengan arsitektur N-layer, dimana object entities dari Data Access Layer dimapping menggunakan AutoMapper terhadap object Dto pada Business Layer. Mapping tersebut perlu kita perhatikan, terutama jika kita menggunakan lazy loading pada Entity Framework sebagai ORM dan navigasi properties yang kita mapping adalah collection.
public class Price { public Price() { this.Benefits = new HashSet(); this.PriceCalculates = new HashSet(); } public int Id { get; set; } public string Name { get; set; } public virtual PriceTemplate PriceTemplate { get; set; } public virtual ICollection Benefits { get; set; } public virtual ICollection PriceCalculates { get; set; } }
public class PriceDto { public PriceDto() { this.BenefitIds = new List(); this.PriceCalculates = new List(); } public int Id { get; set; } public string Name { get; set; } public PriceTemplate PriceTemplate { get; set; } public List BenefitIds { get; set; } public List PriceCalculates { get; set; } }
Contoh, kita meregistrasi mapping antara class Price entities dengan class PriceDto sebagai object Dto.
CreateMap<Price, PriceDTO>() .ForMember(dest => dest.PriceCalculates, opt => opt.Ignore()) .ForMember(dest => dest.BenefitsId, opt => opt.MapFrom(src => src.Benefits.Select(benefit => benefit.Id).ToList())) .IgnoreAllNonExisting();
Ternyata sesuai requirement pada tampilan list aplikasi kita, kita tidak perlu menampilkan data PriceCalculates. Hanya perlu ditampilkan pada form detail saja.
Jika generic repository kita menggunakan lazy loading, maka akan ada hit ke database sebanyak jumlah data yang ditampilkan. Untuk menghindari issue tersebut,Ignore mapping semua navigation properties yang tidak digunakan pada Dto. Kenapa perlu diignore secara explisit padahal sudah menggunakan extension .IgnoreAllNonExisting() ? karena secara default automapper akan melakukan mapping jika property class sumber dan destinasi namanya sama (tidak case sensitif).
CreateMap<Price, PriceDTO>() .ForMember(dest => dest.PriceCategories, opt => opt.Ignore()) .ForMember(dest => dest.PriceCalculates, opt => opt.Ignore()) .ForMember(dest => dest.BenefitsId, opt => opt.MapFrom(src => src.Benefits.Select(benefit => benefit.Id).ToList())) .IgnoreAllNonExisting();
Perhatikan, kita tetap butuh map dari navigaiton property Benefits pada Price terhadap property BenefitsId. Agar data Benefits diambil sekaligus pada saat melakukan query Price, tambahkan sebagai include properties
var includedProperties = "Benefits"; var prices = _priceRepo.Get(filter, orderBy, includedProperties);
sebagai tambahan, implementasi dari method Get pada Price Repository di atas (_priceRepo) seperti berikut ini, lihat Generic Repository Entity Framework Part I
public virtual IEnumerable Get( Expression<Func<Class, bool>> filter = null, Func<IQueryable, IOrderedQueryable> orderBy = null, string includeProperties = "") { IQueryable query = dbSet; if (filter != null) { query = query.Where(filter); } foreach (var includeProperty in includeProperties.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } if (orderBy != null) { return orderBy(query).ToList(); } else { return query.ToList(); } }