پیاده سازی object to object mapping با AutoMapper

نگاشت یک شی به یک شی مشابه دیگر معمول است. همچنین خسته کننده و تکراری است زیرا به طور کلی هر دو کلاس دارای ویژگی های یکسان یا مشابه هستند که به یکدیگر نگاشت شده اند. به مثال زیر توجه کنید:public class UserAppService : ApplicationService { private readonly IRepository<User, Guid> _userRepository; public UserAppService(IRepository<User, Guid> userRepository) { _userRepository = userRepository; } public void CreateUser(CreateUserInputDto input) { //Manually creating a User object from the CreateUserInputDto object var user = new User { Name = input.Name, Surname = input.Surname, EmailAddress = input.EmailAddress, Password = input.Password }; _userRepository.Insert(user); } }خوب CreateUserInputDto یک کلاس DTO ساده و User یک موجودیت ساده است. کد بالا یک موجودیت User از شی input ایجاد می کند. موجودیت Userدارای ویژگی های بیشتری در یک برنامه دنیای واقعی خواهد بود و ایجاد دستی آن خسته کننده و مستعد خطا خواهد شد. همچنین باید هنگام افزودن ویژگی های جدید به کلاس های User و CreateUserInput، کد نگاشت را تغییر دهید.ما می توانیم از یک کتابخانه برای مدیریت خودکار این نوع نگاشت ها استفاده کنیم.AutoMapper یکی از محبوب‌ترین کتابخانه‌های object mapping است.و اما AutoMapper چیست؟کتابخانه Automapper به شما کمک می کند تا داده ها را از یک شی به شی دیگر کپی کنید. از نگاشت به طرق مختلف پشتیبانی می‌کند، مانند نگاشت یک property name مشابه یا متفاوت، همچنین می‌تواند انواع property data types مختلف را نگاشت کند، و یا می‌تواند یک شی منفرد یا یک فهرست از اشیا را map کند.حالا Automapper چگونه کار می کند؟برای نصب Automapper می توانید از Nuget یا از Package Manager آن را دانلود کنید.PM> Install-Package AutoMapper پیکربندی و ریجستر نمودن Mapping در Asp.Net Core 6 : فایل Program.cs :builder.Services.AddAutoMapper(typeof(<name-of-profile>))من از Profiles برای انجام تنظیمات mapping استفاده کردم.اما Profile Instances چیست؟یک راه خوب برای سازماندهی پیکربندی های Mapping ، استفاده از Profile ها است. کلاس هایی را ایجاد کنید که از کلاس پایه Profile(در کتابخانه Automapper) ارث بری می کنند. و در نهایت پیکربندی را در constructorقرار دهید:public class MyProfile : Profile { public MyProfile() { CreateMap<User, CreateUserInputDto>() //and etc ... } }همانطور که در تعریف Automapper گفته شد این کتابخانه می تواند از نگاشت بین اشیاء به طرق مختلف پشتیبانی کند، مانند نگاشت یک property name مشابه یا متفاوت، همچنین می‌تواند انواع property data types مختلف را نگاشت کند، و یا می‌تواند یک شی منفرد یا یک فهرست از اشیا را map کند.به متد (TSource,TDestination)CreateMap توجه کنید یک پیکربندی mapping از نوع TSource به نوع TDestination ایجاد می کند. شما می توانید مدل های زیادی را که نیاز به mapping دارند را در کلاس MyProfile ثبت کنید.انواع Mappingنوع Normal mapping: نگاشت نرمال این است که تمام propertyها در هر دو مدل(در این مثال User و CreateUserInputDto) دارای یک نام و نوع داده هستند.این mapping از منظر عملکرد بسیار آسان و سریع است.برای اجرای mapping بصورت نرمال می توانیم مرحله به مرحله به صورت زیر اقدام کنیم:اول مانند آنچه در پیکربندی و ریجستر نمودن Mapping گفته شد اقدام به Register نمودن آن می کنیم.سپس در business code خود چیزی شبیه به زیر را خواهیم داشت : public async Task<UserDto> GetAsync(Guid id) { var user= await _userRepository.GetAsync(id); return ObjectMapper.Map<User, UserDto>(user); }اما اگر بخواهیم لیستی از اشیا را Map کنیم چطور :return ObjectMapper.Map<List<User>, List<UserDto>>(lstuser);نوع Complex mapping : نوع اول : یک نگاشت پیچیده عبارت است از نگاشت یک یا چند prpperty که همنام نیستند. اکنون می خواهیم mapping ویژگی ID را با ویژگی UsertId را انجام دهیم: CreateMap<UserDto, User>(). .ForMember( dst => dst.Id, act => act.MapFrom(src => src.UserId));نوع دوم : یک نگاشت پیچیده می تواند mapping یک submodel در یک submodel دیگر باشد.public class OrderModel { public decimal Total { get; set; } public CustomerModel CustomerModel { get; set; } } public class CustomerModel { public string Name { get; set; } } public class OrderDto { public decimal Total { get; set; } public CustomerDto Customer { get; set; } } public class CustomerDto { public string Name { get; set; } }اکنون می‌خواهیم OrderDto را به OrderModel نگاشت کنیم، می‌توانم mapping را به صورت زیر ثبت کنم:CreateMap<OrderDto, OrderModel>() .ForMember(dst => dst.CustomerModel, act => act.MapFrom(src => src.Customer));هدف از این پست صرفا آشنایی با object to object mapping در ساده ترین حالت ها بودو اما انواع بسیار دیگری از Complex mapping وجود دارد که می توانید مستندات آن را در سایت automapper.org مطالعه کنید.ProjectionNested MappingsLists and ArraysConstructionFlatteningReverse Mapping and Unflatteningand etc ...تست performance از Automapperخوب mapping دستی بسیار سریع است، با یک میلیون سطر و تعداد 50 property فقط 41 میکروثانیه طول می کشد.(اعداد و ارقام با توجه به قدرت سخت افزاری هر سیستم می تواند متفاوت باشد) اما همین ارقام را با Automapper مقایسه کنید !!!!شما می توانید سورس کد تست performace Manual map and Automapper را از اینجا دانلود کنید.در نهایت اگر می‌خواهید کدی کوتاه و سریع(از بابت زمان develop) داشته باشید، می‌توانید از Automapper استفاده کنید، اما اگر به performance بالا نیاز دارید، می‌توانید manual mapper را انتخاب کنید. بیشتر بخوانید : پیاده سازی Application Services در DDDبیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Corehttps://zarinp.al/farshidazizi