چطور هر بار یک Implementation از یک سرویس رو بر اساس یک شرط استفاده کنیم؟

چند وقت پیش داشتم روی پروژه ای کار میکردم که موضوعی پیش اومد و با خودم گفتم شاید برای شما هم جالب و کاربردی باشه. موضوع از این قرار بود که ما یک سرویس ارسال SMS داشتیم که در اون چند متد متفاوت برای ارسال داشتیم و چند provider هم داشتیم که بر اساس یک شرط خاص بین اون ها سوئیچ میکردیم. در یک حالت ساده تصور کنید که ما یک تنظیمی رو در appsetting پروژه گذاشتیم و هر بار که اون رو تغییر میدیم از یک Provider برای ارسال SMS استفاده می شه.من از این روش استفاده کردم. اگر شما از روش دیگه ای استفاده میکنید خوشحال میشم اون رو به اشتراک بگذارید.من برای اینکه مشخص کنم از چه Provider ای استفاده بشه در فایل appsettings یک چنین تنظیماتی ایجاد کردم : &quotSmsSettings&quot: { &quotDefualtProvider&quot: &quotKavenegar&quot, &quotKavenegarSetting&quot: { &quotApiKey&quot: &quotMyApiKey&quot, &quotSenderLine&quot: &quot123456&quot }, &quotFarazSetting&quot: { &quotUserName&quot: &quotMyUserName&quot, &quotPassword&quot: &quotPassword&quot, &quotSenderLine&quot: &quot123456&quot } }و یک اینترفیس هم ایجاد کردم که در اون متد مورد نظرم پیاده شده : public interface IGetwaySmsService { Task SendSimpleSms(SmsMessage message); }در چند تا سرویس که داشتم هم این متد ها رو پیاده سازی کردم ( البته که پیاده سازی هر متد توی هر سرویس روش خاص خودش رو داره اما اینجا من به صورت خیلی ساده اونها رو ایجاد کردم).برای مثال کلاس اول رو به این شکل پیاده کردم: public class FarazService : IGetwaySmsService { private readonly AppSettings _settings; public FarazService(IOptionsSnapshot<AppSettings> settings) { _settings = settings.Value; } public async Task SendSimpleSms(SmsMessage message) { await Farazsms.SendSmsAsync(message.PhoneNumber, message.Text); } }و کلاس دوم رو هم به این شکل : public class Kavenegar : IGetwaySmsService { private readonly AppSettings _settings; public Kavenegar(IOptionsSnapshot<AppSettings> settings) { _settings = settings.Value; } public async Task SendSimpleSms(SmsMessage message) { await KavenegarSms.SendSmsAsync(message.PhoneNumber, message.Text); } }حالا نوبت به کلاسی هست که قراره این اینترفیس رو استفاده کنه و بر اساس تنظیماتی که در appsettings گذاشتم انتخاب کنه که از یکی از این سرویس ها استفاده کنه. تنظیمات appsettings رو خوندم و در کلاس AppSettings گذاشتم. بعد برای اینکه مشخص کنم Interface ای که دارم ازش استفاده می کنم قرار هست که از کدوم یکی از این سرویس های Implement شده استفاده کنه، یک delegate به نام getwaysmsService ایجاد کردم و در زمانی که میخوام یکی از متد های اینترفیس رو صدا بزنم اون رو با مقداری که توی appsetting در DefualtProvider قرار دادم مقدار دهی کردم(شما میتونید از هر روش دیگه ای برای این مقدار دهی استفاده کنید). به این صورت :public class NotificationSenderService { private readonly AppSettings _settings; private readonly Func<string, IGetwaySmsService> _getwaysmsService; public BackgroundNotificationSenderService( Func<string, IGetwaySmsService> getwaysmsService ) { _getwaysmsService = getwaysmsService; _settings = settings.Value; } public async Task SendSimpleSms(SmsMessage message) { var smsService = _getwaysmsService(_settings.SmsSettings.DefualtProvider); await smsService.SendSimpleSms(message); } }در نهایت باید سرویس هام رو در Startup رجیستر کنم. اما نکته ای که اینجا باید بهش دقت کنیم این هست که لازمه سرویس ها رو به صورت Transient رجیستر کنیم تا در هر بار درخواست بررسی کنه که از کدوم سرویس میخواد استفاده کنه. services.AddTransient<FarazService>(); services.AddTransient<KavenegarService>(); services.AddTransient<Func<string, IGetwaySmsService>>(serviceProvider => key => { switch (key) { case &quotKavenegar&quot: return serviceProvider.GetService<KavenegarService>(); case &quotFaraz&quot: return serviceProvider.GetService<FarazService>(); default: throw new NotImplementedException(); } });نکته ای که بازم باید تکرار بشه این هست که در اینجا یک key پاس داده شده و بر اساس همین key هست که مشخص میشه از کدوم سرویس استفاده بشه. اگر کلاس بالا رو نگاه کنید این key رو از طریق apsettings دارم میخونم. شما میتونید به هر روش دیگه ای این key رو پاس بدید :) .به این ترتیب شما هم میتونید بر اساس یک شرط هر بار یک Implementation از یک سرویس رو استفاده کنید.