AutoMapper یک کتابخانه در داتنت است که برای نگاشت (Map) خودکار اشیاء استفاده میشود. این کتابخانه به شما اجازه میدهد تا بهطور خودکار دادهها را بین دو نوع مختلف از اشیاء انتقال دهید. این کار میتواند بسیار مفید باشد زمانی که میخواهید دادهها را از مدلهای دامنه (Domain Models) به مدلهای نمایش (View Models) یا DTO ها (Data Transfer Objects) منتقل کنید.
ابتدا باید از طریق nuget آن را به پروژه خود اضافه کنید
پکیج :
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
اگر از معماری clean architecture استفاده میکنید باید آن را به پروژه Domain خود اضافه کنید. این مقاله اگر Automapper نصب ندارین ،زمانی که پکیج بالا نصب میکنید همراهش Automapper نصب میکنه.
مرحله دوم این هست که باید بریم در فایل startup.cs و در بخش ConfigureServices اتومپر (Automapper) رو صدا بزینم.
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper();
services.AddMvc();
}
مرحله سوم اضافه کردن profile ها ست . یک profile در واقع نقشه (map) هست که میگه چه کلاسی باید به چه کلاسی تبدیل کنی ! حالا ما یک کلاس به نام DomainProfile داریم که در سازندش یک mapping تنظیم شده:
public class DomainProfile : Profile
{
public DomainProfile()
{
CreateMap<DomainUser, UserViewModel>();
}
}
وقتی application اجرا بشه Automapper از طریق کد شما تمام کلاس هایی که از کلاس Profile ارث بری شده پیدا می کند و configuration آنها را load میکند. به همین سادگی به همین خوشمزگی!
مرحله چهار استفاده از IMapper . وقتی پیکربندی (configuration) پروفایل های شما بارگذاری (load) شد. باید interface (واسط) IMapper مانند DI Framework های
دیگر (Dependency Injection) تزریق (Inject) کنید. که این کار رو از طریق سازنده (constructor) انجام میدیم و به همه اشیا نقشه ها دسترسی خواهیم داشت:
public class HomeController : Controller
{
private readonly IMapper _mapper;
public HomeController(IMapper mapper)
{
_mapper = mapper;
}
public IActionResult Index()
{
//Do some work here.
//And when we want to map do something like the following.
var result = _mapper.Map<TypeIWantToMapTo>(originalObject);
}
}
دوباره بنظر میرسد کار خارق العاده ای اتفاق افتاده است. ما هیچ وقت IMapper را در ServiceCollection ثبت (register) نکردیم . اما پکیجی با نام AddAutoMapper که در بخش ConfigureServices ثبت کردیم به صورت اتوماتیک هندل میشود.
دامنه (Scopes)
آخرین چیزی که در نهایت میماند دامنه های داخل Automapper چگونه کار میکنند. پیکربندی های شما (مانند Profile های Automapper ) سینگلتون singleton هستند. به این دلیل که در اجرای برنامه فقط یکبار load میشوند.
واسط IMapper خودش Scoped هست. از نظر ASP.net ، به این معنی است که برای هر درخواست منحصر بفرد(individual) ، یک IMapper جدید ایجاد می شود اما در سرتاسر برنامه برای آن درخواست به اشتراک گذاشته می شود (همون Scope در aspnetcore DI هست) (بر عکس transient که یک درخواست منحصربفرد در سرتاسر برنامه هرزمان آن را تقاضا کند برای آن درخواست نمونه جدید میسازد).بنابراین اگر از IMapper در داخل یک controller و در داخل یک service برای یک درخواست واحد (single request) استفاده کنید ، آنها از همان یک نمونه IMapper استفاده می کنند.
اما چیزی که واقعا مهم است اینکه همه چیز از IValueResolver ، ITypeConverter یا IMemberValueResolver مشتق(derives) شده است، transient scope هستند.آنها همچنین با استفاده از .NET Core Service Collection DI ایجاد می شوند.
کد زیر را ببینید،برای resolve کردن یک mapping خاص استفاده می شود که به وسیله آن می خواهیم یک username براساس id مدل، map سراسری کنیم.برای این کار
public class UsernameResolver : IValueResolver<DomainUser, UserViewModel, string>
{
private readonly IUserRepository _userRepository;
public UsernameResolver(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public string Resolve(DomainUser source, UserViewModel destination,string destMember,
esolutionContext context)
{
return _userRepository.GetById(source.Id).Username;
}
}
از آنجا که این به عنوان نمونه transient با استفاده از .NET Core’s Service Collection ،مخزن ما (repository) نیز به درستی resolve شد و ما قادر خواهیم بود map کردن هایی را انجام دهم که بیش از map کردن سراسری مقادیر ساده باشند.
حال در گیت هاب automapper یک پکیجی را قرار داده که Automapper روی efcore سوار می کنه
آدرس پکیج
Install-Package AutoMapper.Collection.EntityFrameworkCore
این پکیج با پکیجی که قبل تر معرفی کردیم سازگار است و همچنین با DI خود netcore.
به عنوان مثال:
var services = new ServiceCollection();
services
.AddEntityFrameworkInMemoryDatabase()
.AddDbContext<DB>();
services.AddAutoMapper((serviceProvider, automapper) =>
{
automapper.AddCollectionMappers();
automapper.UseEntityFrameworkCoreModel<DB>(serviceProvider);
}, typeof(DB).Assembly);
var serviceProvider = services.BuildServiceProvider();
توجه: expressionهای تعریف شده توسط کاربر ، expression های اصلی کلید (primary key) را بازنویسی می کند.
در مورد مقایسه با یک Entity موجود برای بروزرسانی چه می توان گفت؟
پکیج Automapper.Collection.EntityFrameworkCore این کار را از طریق extension method از DbSet انجام می دهد.
ترجمه برابری بین Data Transfer Object (مخفف شدش Dto) و شی EF (همون Entity Framework) به یک expression فقط EF با استفاده از مقادیر dto به عنوان ثابت ها است.
dbContext.Orders.Persist(mapper).InsertOrUpdate<OrderDTO>(newOrderDto);
dbContext.Orders.Persist(mapper).InsertOrUpdate<OrderDTO>(existingOrderDto);
dbContext.Orders.Persist(mapper).Remove<OrderDTO>(deletedOrderDto);
dbContext.SubmitChanges();
توجه: این کار با تبدیل OrderDTO به Expression <Func <Order، bool ”و استفاده از آن برای پیدا کردن نوع تطبیق در پایگاه داده انجام می شود. همچنین می توانید شیء ها را به expression ها نیز map کنید.
(ارسال تغییرات) submit changes بطور خودکار صدا زده نمیشود.