این روش منحصر به Nop نیست و امکان استفادهی از آن بر روی هر سورس دیگری نیز وجود دارد. همچنین اگر در رابطه با NopCommerce اطلاعاتی ندارید، میتوانید از اینجا جهت آشنا شدن با این فروشگاه ساز Asp.net core استفاده کنید.
مراحل پیاده سازی
یک اینترفیس را به اسم ICustomService ایجاد کردم که یک Prop به اسم InjectType دارد و مشخص میکند به چه صورتی این سرویس به ServiceCollection تزریق شود. از طرفی با استفاده از Order، الویت اضافه شدن سرویس به ServiceCollection را مشخص میکنیم و در نهایت با ImplementationType مشخص میکنیم سرویسی که اضافه شده، باید به یک اینترفیس Map شود یا خیر؟ اما مهمتر از اینکه ویژگیهای تزریق وابستگی مشخص شود، مشخص میکند چه سرویسهایی توسط ما اضافه شدهاند و از سرویسهای nop تفکیک میشوند.
قانون اول
برای هر سرویسی که ایجاد میکنیم و میخواهیم به DI معرفی کنیم، آن سرویس باید ICustomService را پیاده سازی کرده باشد؛ دقیقا به خاطر دو دلیلی که در بالا به آنها اشاره شد.
قانون دوم
هر کلاسی که Interface مرتبط به سرویسها را پیاده سازی میکند، باید prop InjectType را در سازندهی خودش مقدار دهی کند. بدین شکل متوجه میشویم از چه طریقی باید تزریق انجام شود. تا اینجا یک چارچوب را مشخص کردیم تا سرویسها را بتوانیم تشخیص دهیم\ اما هنوز کار اصلی باقی ماندهاست. برای نمونه میتوان کد زیر را در نظر گرفت :
برای پیاده سازی سرویس ایجاد شده، کد زیر را ایجاد میکنیم :
تعیین نقطه شروع
کد زیر در واقع نقطهی اتصال سرویسهای نوشته شده و اتمام کار تزریق وابستگی است. با توجه به پیاده سازیهای انجام شدهی توسط سرویسها میتوان با Reflection سرویسهای نوشته شده را تشخیص داد که در نهایت با ویژگیهایی که در سرویسها پیاده سازی شده موجود است، به ServiceCollection اضافه میشوند.
نکتهی آخر آن که این داستانها صرفا برای سرویسهایی هست که توسط برنامه نویس به پروژهی Nop اضافه میشود.
لینک گیتهاب
همانطور که در جریان هستید، برای اینکه بحث DI را در پروژه داشته باشیم، باید به ازای هر سرویس مشخص کنیم که کدام اینترفیس، به کدام کلاس، map شود. به بیان دیگر باید مشخص کرد هر وقت یک شیء از Container درخواست شد، از چه کلاسی باید این شیء ساخته شود؛ در عینحال باید LifeTime وجود شیء در حافظه نیز مشخص شود. حال تصور کنید تعداد سرویسهای شما در حال زیاد شدن است. در این حالت مجبور هستید دائما این سرویسها را ثبت کنید؛ علاوه بر اینکه باید کدهای تکراری را جهت تعریف این سرویسها بنویسید و باید بهخاطر بسپارید که سرویس جدید را ثبت کنید. در این مقاله تلاش بر این است تا دیگر نیازی به تعریف کردن تک تک سرویسها نباشد؛ بهطوری که با رعایت دو قانون کلی بتوان سرویسها را به صورت خودکار ثبت کرد.
یک اینترفیس را به اسم ICustomService ایجاد کردم که یک Prop به اسم InjectType دارد و مشخص میکند به چه صورتی این سرویس به ServiceCollection تزریق شود. از طرفی با استفاده از Order، الویت اضافه شدن سرویس به ServiceCollection را مشخص میکنیم و در نهایت با ImplementationType مشخص میکنیم سرویسی که اضافه شده، باید به یک اینترفیس Map شود یا خیر؟ اما مهمتر از اینکه ویژگیهای تزریق وابستگی مشخص شود، مشخص میکند چه سرویسهایی توسط ما اضافه شدهاند و از سرویسهای nop تفکیک میشوند.
namespace Nop.Services { public interface ICustomService { protected InjectType Inject { get; } protected int Order { get; } protected ImplementationType implementationType { get; } } public enum ImplementationType { WithInterface = 0, WithoutInterface = 1 } public enum InjectType { Scopped=0, Transit=1, SingleTon=2 } }
قانون اول
برای هر سرویسی که ایجاد میکنیم و میخواهیم به DI معرفی کنیم، آن سرویس باید ICustomService را پیاده سازی کرده باشد؛ دقیقا به خاطر دو دلیلی که در بالا به آنها اشاره شد.
قانون دوم
هر کلاسی که Interface مرتبط به سرویسها را پیاده سازی میکند، باید prop InjectType را در سازندهی خودش مقدار دهی کند. بدین شکل متوجه میشویم از چه طریقی باید تزریق انجام شود. تا اینجا یک چارچوب را مشخص کردیم تا سرویسها را بتوانیم تشخیص دهیم\ اما هنوز کار اصلی باقی ماندهاست. برای نمونه میتوان کد زیر را در نظر گرفت :
namespace Nop.Services { public interface IMyCustomService: ICustomService { int ok(); } }
namespace Nop.Services { public class MyCustomService : IMyCustomService { public InjectType Inject { get; } public int Order { get; } public ImplementationType implementationType { get; } public MyCustomService() { implementationType = ImplementationType.WithInterface; Inject = InjectType.Scopped; Order = 1; } public int ok() { return 10; } } }
تعیین نقطه شروع
باید نقطه شروع به کار Nop را پیدا کنیم. از آنجایی که با معماری Nop جلو میرویم، با کمی بررسی و دیدن کدها، به کلاسی میرسیم به اسم NopStartup در قسمت Nop.Web.Framework. مسیر دقیق آن: Nop.Web.Framework\Infrastructure\NopStartup.cs. حالا این کلاس چیست؟ در واقع هر کلاسی که از سرویس INopStartup ارث بری کرده باشد، اولویت پیدا میکند و قبل از کدهای دیگر اجرا میشود. باید کلاس جدیدی را به اسم مثلا CustomDependencyInjection ایجاد کنیم، با این تفاوت که حتما از کلاس NopStartup ارث بری کرده باشد و همچنین حتما باید متدی را به اسم ConfigureServices، بازنویسی کند. حالا داخل متدی که گفتم باید شروع کنیم به کار.
کد زیر در واقع نقطهی اتصال سرویسهای نوشته شده و اتمام کار تزریق وابستگی است. با توجه به پیاده سازیهای انجام شدهی توسط سرویسها میتوان با Reflection سرویسهای نوشته شده را تشخیص داد که در نهایت با ویژگیهایی که در سرویسها پیاده سازی شده موجود است، به ServiceCollection اضافه میشوند.
namespace Nop.Web.Framework.Infrastructure { public class CustomDependencyInjection : NopStartup { private static bool IsSubInterface(Type t1, Type t2) { if (!t2.IsAssignableFrom(t1)) return false; if (t1.BaseType == null) return true; return !t2.IsAssignableFrom(t1.BaseType); } public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) { //-------------Get All Services------------- var asm = AppDomain.CurrentDomain .GetAssemblies() .Single(x => x.FullName.Contains("Nop.Services")); //-------------find Services that inheriance of ICustomService------------- var types = asm.DefinedTypes.Where(x => IsSubInterface(x, typeof(ICustomService))); //-----------Get All Custom Service Classess------- var allRelatedClassServices = types .Where(x => x.IsClass) .OrderBy(x=>(Int32)x.GetProperty("Order") .GetValue(Activator.CreateInstance(x), null)); //-----------Get All Custom Service Interfaces------- var allRelatedInterfaceServices = types.Where(x => x.IsInterface); //-----------Matche Class Services To Related Interface Services------- TypeInfo interfaceService=null; foreach (var classService in allRelatedClassServices) { //-----------detect Implementation Type for service----------- var implementationValue = (ImplementationType)classService.GetProperty("implementationType") .GetValue(Activator.CreateInstance(classService), null); //-----------detect inject type for service----------- var InjectValue = (InjectType)classService.GetProperty("Inject") .GetValue(Activator.CreateInstance(classService), null); //-----------get related interface for service class----------- if (implementationValue == ImplementationType.WithInterface) interfaceService = allRelatedInterfaceServices.Single(x => x.Name == $"I{classService.Name}"); //----------finally Add Custom Service To Service Collection----------- switch (InjectValue) { case InjectType.Scopped: if(interfaceService!=null) services.AddScoped(interfaceService, classService); else services.AddScoped(classService); break; case InjectType.Transit: if (interfaceService != null) services.AddTransient(interfaceService, classService); else services.AddTransient(classService); break; case InjectType.SingleTon: if (interfaceService != null) services.AddSingleton(interfaceService, classService); else services.AddSingleton(classService); break; default: break; } interfaceService = null; } } } }
لینک گیتهاب