آموزش استفاده AutoMapper در ASP.Net Core

Auto Mapper

 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) رو صدا بزینم.

مرحله سوم اضافه کردن profile ها ست . یک profile در واقع نقشه (map) هست که میگه چه کلاسی باید به چه کلاسی تبدیل کنی ! حالا ما یک کلاس به نام DomainProfile داریم که در سازندش یک mapping تنظیم شده:

وقتی application اجرا بشه Automapper از طریق کد شما تمام کلاس هایی که از کلاس Profile ارث بری شده پیدا می کند و configuration آنها را load میکند. به همین سادگی به همین خوشمزگی!

مرحله چهار استفاده از IMapper . وقتی پیکربندی (configuration) پروفایل های شما بارگذاری (load) شد. باید interface (واسط) IMapper مانند DI Framework های
دیگر (Dependency Injection) تزریق (Inject) کنید. که این کار رو از طریق سازنده (constructor) انجام میدیم و به همه اشیا نقشه ها دسترسی خواهیم داشت:

دوباره بنظر میرسد کار خارق العاده ای اتفاق افتاده است. ما هیچ وقت 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 سراسری کنیم.برای این کار

از آنجا که این به عنوان نمونه transient با استفاده از .NET Core’s Service Collection ،مخزن ما (repository) نیز به درستی resolve شد و ما قادر خواهیم بود map کردن هایی را انجام دهم که بیش از map کردن سراسری مقادیر ساده باشند.

حال در گیت هاب automapper یک پکیجی را قرار داده که Automapper روی efcore سوار می کنه

آدرس پکیج

این پکیج با پکیجی که قبل تر معرفی کردیم سازگار است و همچنین با DI خود netcore.

به عنوان مثال:

توجه: expressionهای تعریف شده توسط کاربر ، expression های اصلی کلید (primary key) را بازنویسی می کند.

در مورد مقایسه با یک Entity موجود برای بروزرسانی چه می توان گفت؟

پکیج Automapper.Collection.EntityFrameworkCore این کار را از طریق extension method از DbSet انجام می دهد.

ترجمه برابری بین Data Transfer Object (مخفف شدش Dto) و شی EF (همون Entity Framework) به یک expression فقط EF با استفاده از مقادیر dto به عنوان ثابت ها است.

توجه: این کار با تبدیل OrderDTO به Expression <Func <Order، bool ”و استفاده از آن برای پیدا کردن نوع تطبیق در پایگاه داده انجام می شود. همچنین می توانید شیء ها را به expression ها نیز map کنید.

(ارسال تغییرات) submit changes بطور خودکار صدا زده نمیشود.

آموزش استفاده از CQRS و Mediator در Asp.Net Core

mediator-cqrs-microservice-mediatr

قبل ار اینکه به پیاده سازی CQRS بپردازیم کمی به علت استفاده از آن میپردازیم. هدف از استفاده از الگوی CQRS (Command and Query Responsibility Segregation) کدنویسی بهینه تر در بخشهایی از پروژه که دارای پیچیدگی زیادی دارند می باشند.

قبل ار اینکه به پیاده سازی CQRS بپردازیم کمی به علت استفاده از آن میپردازیم. 
هدف از استفاده از الگوی CQRS (Command and Query Responsibility Segregation) کدنویسی بهینه تر در بخشهایی از پروژه که دارای پیچیدگی زیادی دارند می باشند.لذا در این بخش بصورت خیلی ساده و به دور از هرگونه توضیحات اضافه که فقط باعث سختتر شدن فهم مطالب برای شما هنرجویان میشود ، خواهیم پرداخت.
در این سناریو پیاده سازی عملیات  CRUD برای موجودیت Product  با استفاده از CQRS و Mediator انجام خواهد شد.
دقت داشته باشید هنگام پیاده سازی عملیات  CRUD با استفاده از الگوی CQRS ، عملیات خواندن داده ها (Read) را درون پوشه Query قرار می دهیم و سایر دستورات  ( Insert  / Update یا Delete  ) را درون پوشه Comman قرار می دهیم لذا در این مثال پس از ایجاد پروژه یک پوشه به نام CQRS ایجاد میکنیم و سپس درون آن پوشه ای به نام ProductCommandQuery و درون آن دو پوشه به نامهای Command  و Query ایجاد میکنیم.
در این مثال موجودیت Product با صفات زیر را در نظر بگیرید.

 نکته : تنظیمات رشته اتصال درون app.setting  و عملیات Migration  انجام شود.
برای فراخوانیهای سرویسهای درون CQRS از یک واسط به نام Mediator استفاده میکنیم لذا در این قسمت از برنامه از طریق nuget کتابخانه مورد نظر را به پروژه اضافه میکنیم. 

 پیاده سازی الگوی ریپازیتوری(Repository) : 
درون پوشه ای به نام Repositositories کلاسی به نام IRepository ایجاد میکنیم و اینترفیس IProductRepository را بصورت زیر ویرایش میکنیم.

البته بر اساس نیاز میتوانید امضای متدهای بیشتری را درون آن قرار دهید.
سپس درون پوشه Repositories کلاس  ProductRepositories را بصورت زیر درج میکنیم.

پیاده سازی UnitOfwork : 

جهت جدا سازی عملیات ذخیره سازی داده ها از الگوی Repository باید از مفهوم UnitOfWork استفاده نماییم. برای پیاده سازی آن ابتدا یک  اینترفیس به نام IUnitOfwork  بصورت زیر ایجاده میکنیم.

سپس کلاس UnitOfwork را بصورت زیر درج میکنیم.

برای پیاده سازی عملیات ذخیره سازی داده با استفاده از CQRS کلاسی به نام SaveProductCommand درون پوشه Command ایجاد میکنیم.

 کلاس SaveProductCommand برای دریافت داده ها از ورودی مورد استفاده قرار میگیرد لذا باید از این کلاس جهت پر کردن مقادیر ورودی استفاده کنیم.
کلاس SaveProductCommandResponse جهت بازگرداندن خروجی مورد استفاده قرار میگیرد لذا شما بر اساس نیاز پروژه فیلدهای مورد نظر را تعریف نمایید.
کلاس SaveProductCommandHandler جهت اجرای دستورات با استفاده از  mediator مورد استفاده قرار میگیرد. که با فراخوانی متد Handle آن دستورات اجرا شده و داده ها درون دیتابیس ذخیره میشود.
اکنون یک Api به نام ProductController جهت استفاد و اجرای برنامه بصورت زیر ایجاد میکنیم.

 دقت نمایید با استفاده از mediator سرویس مورد نظر را درون متد Send اجرا میکنیم.