تزریق وابستگیها در ASP.NET Core - بخش 3 - ثبت و واکشی تنظیمات
نویسنده: رضا پویا
تاریخ: ۱۳۹۹/۰۳/۰۴ ۲:۵۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
"ApplicationName": "Dependency Injection Demo",
"GreetingMessage": "Welcome to Dependency Injection Demo",
"AllowedHosts": "*",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}, برای سادگی کار، با بخش Logging
کاری نداریم . اکنون فایل AppConfig.cs را به برنامه اضافه میکنیم:
namespace AspNetCoreDependencyInjection.Configs
{
public class AppConfig
{
public string ApplicationName { get; set; }
public string GreetingMessage { get; set; }
public string AllowedHosts { get; set; }
}
} برای دسترسی بهتر میتوانیم سازندهی کلاس Startup را تغییر دهیم:
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public IServiceCollection Services { get; set; }
public Startup(IWebHostEnvironment environment)
{
var builder = new ConfigurationBuilder()
.SetBasePath(environment.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true)
.AddEnvironmentVariables();
this.Environment = environment;
this.Configuration = builder.Build();
} var appSettingsFile = environment.IsProduction() ? "appsettings.json" : "appsettings_dev.json";
var builder = new ConfigurationBuilder()
.SetBasePath(environment.ContentRootPath)
.AddJsonFile( appSettingsFile , optional: true)
.AddEnvironmentVariables(); services.AddSingleton(services => new AppConfig {
ApplicationName = this.Configuration["ApplicationName"],
GreetingMessage = this.Configuration["GreetingMessage"],
AllowedHosts = this.Configuration["AllowedHosts"]
}); در کد بالا در هنگام اجرای برنامه، یک نمونه از کلاس AppConfig را با طول حیات Singleton ثبت کردیم و Property های این شیء را به وسیلهی ایندکس Configuration[“FieldName”]، تک تک پر کردیم.
حالا میتوانیم سرویس AppConfig را
در هر کلاسی از برنامهی خودمان تزریق و از آن استفاده کنیم. برای مثل در اینجا یک
کنترلر به نام AppSettingsController ساختم و کلاس فوق را به آن تزریق کردم:
public class AppSettingsController : Controller
{
private readonly AppConfig _appConfig;
public AppSettingsController(AppConfig appConfig)
{
_appConfig = appConfig;
}
// codes here …
} "UserOptionConfig": {
"UsersAvatarsFolder": "avatars",
"UserDefaultPhoto": "icon-user-default.png",
"UserAvatarImageOptions": {
"MaxWidth": 150,
"MaxHeight": 150
}
},
"LiteDbConfig": {
"ConnectionString": "Filename=\\Data\\DependencyInjectionDemo.db;Connection=direct;Password=@123456;"
} برای LiteDbConfig
مانند AppConfig عمل میکنیم، ولی در هنگام ثبت آن، به روش زیر عمل میکنیم. تنها تفاوتی
که وجود دارد، نحوهی دستیابی به فیلدهای درونی فایل JSON به وسیلهی شیء Configuration
است:
services.AddSingleton(services => new LiteDbConfig
{
ConnectionString = this.Configuration["LiteDbConfig:ConnectionString"],
}); اکنون برای استفادهی از مدخل UserOptionConfig،
کلاسهای زیر را میسازیم:
namespace AspNetCoreDependencyInjection.Configs
{
public class UserOptionConfig
{
public string UsersAvatarsFolder { get; set; }
public string UserDefaultPhoto { get; set; }
public UserAvatarImageOptions UserAvatarImageOptions { get; set; }
}
public class UserAvatarImageOptions
{
public int MaxHeight { get; set; }
public int MaxWidth { get; set; }
}
} جداسازی بخشهای مختلف تنظیمات پیکربندی باعث میشود تا بتوانیم دو اصل اساسی از طراحی نرم افزار را رعایت کنیم :
در اینجا نیاز به استفاده از پکیج Microsoft.Extensions.Options.ConfigurationExtensions را داریم که به صورت درونی در ASP.NET Core تعبیه شده است.
برای ثبت این تنظیمات درون DI Container، از نمونهی جنریک متد Configure در IServiceCollection به صورت زیر استفاده میکنیم:
services.Configure<UserOptionConfig>(this.Configuration.GetSection("UserOptionConfig")); متد GetSection بر اساس نام بخش تنظیمات، خود آن تنظیم و تمامی تنظیمات درونی آن را به صورت یک IConfigurationSection بر میگرداند و متد Configure<TOption> یک IConfiguration را گرفته و به صورت خودکار به TOption اتصال میدهد و سپس این شیء را درون DI Container به عنوان یک IConfigurationOptions<TOption> و با طول حیات Singleton ثبت میکند.
برای دسترس به UserOptionConfig درون کلاس مورد نظر ما، اینترفیس <IOptionMonitor<TOption را به سازندهی کلاس مورد نظر تزریق میکنیم. کد زیر را که نسخهی تغییر یافتهی کلاس AppSettingsController است را مشاهده کنید:private readonly LiteDbConfig _liteDbConfig;
private readonly AppConfig _appConfig;
private readonly UserOptionConfig _userOptionConfig;
public AppSettingsController(AppConfig appConfig ,
LiteDbConfig liteDbConfig ,
IOptionsMonitor<UserOptionConfig> userOptionConfig)
{
_appConfig = appConfig;
_liteDbConfig = liteDbConfig;
_userOptionConfig = userOptionConfig.CurrentValue;
} نکته ای که وجود دارد، کلاسهای تعریف شده برای استفادهی از این الگو باید شرایط زیر را داشته باشند ( مثل کلاس UserOptionConfig ) :
هر دو روش بالا که یکی به
صورت عادی تنظیمات را ثبت میکند و دیگری با استفاده از Option Pattern بخشهای مختلف را ثبت میکند،
مناسب هستند. البته گاهی اوقات فایلهای تنظیمات پروژهی شما در لایههای زیرین (یا درونیتر اگر از onion architecture استفاده میکنید) قرار دارند و شما نمیخواهید
در آن لایهها و لایههای درونیتر، وابستگی به پکیجهای ASP.NET Core ایجاد کنید. در این حالت با در
نظر گرفتن دو اصل ISP و Separation of Concerns ،
به ازای هر بخش مختلف از تنظیمات، فایلهای تنظیمات را در لایههای زیرین/درونی
تعریف کرده، بعد در لایههای بالاتر/بیرونیتر آنها را به درون سرویسها یا کلاسهای مورد نیاز، تزریق کنید. البته مثل همین مثال، ثبت این سرویسها درون برنامهی ASP.NET Core که
معمولا بالاترین/بیرونیترین لایه از پروژهی ما هست، انجام میشود.