در این مقاله، میخواهیم یک configuration provider سفارشی ایجاد کنیم که پیکربندی ما را از پایگاه داده میخواند. ما دیدیم که configuration provider های پیشفرض چطور کار میکنند و حالا قصد داریم configuration provider سفارشی خود را پیاده سازی کنیم.
برای configuration provider سفارشی، از Entity Framework Core به همراه پایگاه داده SQL Server استفاده میکنیم.
اگر میخواهید در طول مسیر این مقاله، ما را دنبال کنید، از نقطه شروع یعنی سورس `پروژه Configuration Provider ها استفاده کنید. برای بررسی پروژه تکمیل شده این مقاله، از پروژه نهایی این مقاله یعنی پروژه تکمیل شده configuration provider سفارشی استفاده کنید.
در این مقاله، عناوین زیر را پوشش میدهیم:
ابتدا، با استفاده از رویکرد solution ،database-first خود را برای پشتیبانی از EF Core ارتقا میدهیم.
راه اندازی EF Core
قبل از اینکه شروع کنیم، چند لحظه، تمام user secret ها و متغیرهای محیطی که در قسمت قبلی ست کرده بودیم را حذف کنیم. زمانی که حذف تمام آنها تمام شد، سپس کارمان را پیش خواهیم برد.
ما اول باید دو Nuget package را نصب کنیم:
1 |
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer -v 3.1.7 |
ما چون میخواهیم از SQL Server instance استفاده کنیم، به این package نیاز داریم و:
1 |
PM> Install-Package Microsoft.EntityFrameworkCore.Tools -v 3.1.7 |
این پکیج را به این دلیل که میخواهیم دیتابیس را از طریق CLI ایجاد و عملیات migration را انجام دهیم نیاز داریم.
ما یک کلاس نیاز داریم که شامل زوجهای key-value از پیکربندی باشد (پوشه Models):
1 2 3 4 5 6 |
public class ConfigurationEntity { [Key] public string Key { get; set; } public string Value { get; set; } } |
و یک کلاس DbContext (پوشه Models):
1 2 3 4 5 6 7 8 9 |
public class ConfigurationDbContext : DbContext { public ConfigurationDbContext(DbContextOptions options) : base(options) { } public DbSet<ConfigurationEntity> ConfigurationEntities { get; set; } } |
ما فقط به یک DbSet از ConfigurationEntity که جدول ما را به دیتابیس مپ میکند نیاز داریم.
حالا ما فقط به set up کردن یک اتصال به سرورمان در متد ()ConfigureServices درون کلاس Startup نیاز داریم:
1 2 |
services.AddDbContext<ConfigurationDbContext>(opts => opts.UseSqlServer(Configuration.GetConnectionString("sqlConnection"))); |
و البته، شما باید connection string را در فایل appsettings.json به دیتابیس خودتان تغییر دهید. اگر از SqlExpress استفاده میکنید، به احتمال زیاد connection string شما این چنین به نظر می رسد:
1 2 3 |
"ConnectionStrings": { "sqlConnection": "server=.\\SQLEXPRESS; database=ExpertMarketCommerce; Integrated Security=true" }, |
تمام کاری که میبایست انجام میدادید همین بود. حالا به سادگی میتوانیم یک migration اولیه را از طریق Package Manager Console انجام دهیم:
1 |
PM> Add-Migration InitialSetup |
و سپس این migration را به دیتابیس اعمال میکنیم:
1 |
Update-Database |
حالا دیتابیس ما ایجاد شده است و آماده است تا برای ذخیره داده های پیکربندی مورد استفاده قرار گیرد.
پیاده سازی یک EF Core Provider سفارشی
برای شروع کار، یک پوشه ConfigurationProviders در پوشه Models خود ایجاد میکنیم تا کلاسهای خود را به درستی گروه بندی کنیم.
سپس، باید در واقع یک configuration provider ایجاد کنیم که از کلاس ConfigurationProvider ارث بری میکند. ما کلاس provider خود را در پوشه ConfigurationProviders ایجاد میکنیم و نام آن را EFConfigurationProvider میگذاریم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
public class EFConfigurationProvider : ConfigurationProvider { public EFConfigurationProvider(Action<DbContextOptionsBuilder> optionsAction) { OptionsAction = optionsAction; } Action<DbContextOptionsBuilder> OptionsAction { get; } public override void Load() { var builder = new DbContextOptionsBuilder<ConfigurationDbContext>(); OptionsAction(builder); using (var dbContext = new ConfigurationDbContext(builder.Options)) { dbContext.Database.EnsureCreated(); Data = !dbContext.ConfigurationEntities.Any() ? CreateAndSaveDefaultValues(dbContext) : dbContext.ConfigurationEntities.ToDictionary(c => c.Key, c => c.Value); } } private static IDictionary<string, string> CreateAndSaveDefaultValues(ConfigurationDbContext dbContext) { var configValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Pages:HomePage:WelcomeMessage", "Welcome to the ProjectConfigurationDemo Home Page" }, { "Pages:HomePage:ShowWelcomeMessage", "true" }, { "Pages:HomePage:Color", "black" }, { "Pages:HomePage:UseRandomTitleColor", "true" } }; dbContext.ConfigurationEntities.AddRange(configValues .Select(kvp => new ConfigurationEntity { Key = kvp.Key, Value = kvp.Value }) .ToArray()); dbContext.SaveChanges(); return configValues; } } |
این کلاس ممکن است در ابتدا کمی ترسناک به نظر برسد، اما آنقدرها هم ترسناک نیست.
سازنده، یک آرگومان دارد که به Action<DbContextOptionsBuilder> optionsAction اشاره میکند. ما جلوتر از کلاس DbContextOptionsBuilder برای تعریف یک context برای دیتابیس خود استفاده میکنیم. ما قبلا این کار را زمانیکه connection string خود را تعریف کرده بودیم، انجام دادیم. ما context options builder را آورده ایم تا این option را در اختیار provider سفارشی خود قرار دهیم.
ما به منظور پرکردن ConfigurationEntity با داده ها از دیتابیس یا اگر دیتابیس خالی باشد، برای ایجاد تعدادی داده های پیشفرض، متد ()Load را override کردیم. این تمام چیزی است که باید باشد.
در مرحله بعد، باید configuration provider خود را به عنوان یک منبع ثبت کنیم. برای این منظور، ما باید اینترفیس IConfigurationSource را پیاده سازی کنیم. بنابراین کلاس EFConfigurationSource را در پوشه ConfigurationProviders ایجاد میکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class EFConfigurationSource : IConfigurationSource { private readonly Action<DbContextOptionsBuilder> _optionsAction; public EFConfigurationSource(Action<DbContextOptionsBuilder> optionsAction) { _optionsAction = optionsAction; } public IConfigurationProvider Build(IConfigurationBuilder builder) { return new EFConfigurationProvider(_optionsAction); } } |
ما فقط باید متد ()Build را پیاده سازی کنیم که در این مثال ما، این متد، configuration provider را با options ای که از طریق سازنده configuration source ارسال کردیم را initialize میکند.
این واقعا سردرگم کننده به نظر میرسد. بنابراین ببینیم که چطور باید configuration provider دیتابیس خود را به لیست configuration source ها اضافه کنیم. ما این را مشابه شیوه قبل انجام می دهیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) .ConfigureAppConfiguration((hostingContext, configBuilder) => { var config = configBuilder.Build(); var configSource = new EFConfigurationSource(opts => opts.UseSqlServer(config.GetConnectionString("sqlConnection"))); configBuilder.Add(configSource); }); |
همانطور که میبینید، ما configuration builder را اینجا به منظور گرفتن IConfiguration میسازیم. ما به آن نیاز داریم، زیرا connection string ما، در فایل appsettings.json ذخیره شده است. حالا میتوانیم یک configuration source را با آن connection string ایجاد کنیم و با استفاده از متد ()configBuilder.Add، آن را به configuration source های موجود اضافه کنیم.
حالا میتوانیم فایل appsettings.json را کمی تمیزتر کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "ConnectionStrings": { "sqlConnection": "server=.\\SQLEXPRESS; database=ExpertMarketCommerce; Integrated Security=true" }, "AllowedHosts": "*" } |
ما قسمت “Pages” را حذف کردیم تا مطمئن شویم که اطلاعات این قسمت از دیتابیس خوانده میشود.
و ما باید متد ()AddDbContext را که قبلا در کلاس Startup استفاده میکردیم را حذف کنیم،زیرا دیگر به آن نیازی نداریم.
1 2 3 4 5 6 7 |
public void ConfigureServices(IServiceCollection services) { //remove!!! services.AddDbContext<ConfigurationDbContext>(opts => opts.UseSqlServer(Configuration.GetConnectionString("sqlConnection"))); ... } |
از آنجایی که دیگر به هیچ migration در این مثال نیاز نداریم، از طریق SQL Management Studio خود یا از طریق SQL Server Object Explorer، یک دیتابیس به نام “ExpertMarketCommerce” به صورت دستی ایجاد کنید.
تمام. حالا برنامه را اجرا کنید.
اجرای برنامه
حالا اگر برنامه را اجرا کنیم، یک breakpoint در کلاس Startup قرار دهید و آبجکت Configuration را بررسی کنید، ما configuration source خود را پیدا خواهیم کرد:
![ef configuration provider در asp.net core](https://expertmarket.ir/wp-content/uploads/2021/08/ef-configuration-provider.png)
عالی.
حال اگر دیتابیس را بررسی کنیم، میبینیم که آن با داده های مورد نظر تکمیل شده است:
![داده های پیکربندی در دیتابیس](https://expertmarket.ir/wp-content/uploads/2021/08/database.png)
اجرا را ادامه میدهیم و میبینیم که برنامه ما طبق انتظار هنوز کار میکند:
![خواندن داده های پیکربندی از دیتابیس](https://expertmarket.ir/wp-content/uploads/2021/08/Home-Page-green.png)
برنامه هنوز به درستی مانند قبل کار میکند! شما میتوانید صفحه را چندبار refresh کنید تا مطمئن شوید که رنگ عنوان هنوز به درستی تغییر میکند.
Conclusion
در این مقاله کوتاه، نحوه پیاده سازی configuration provider سفارشی خود را که مقادیر را از دیتابیس میخواند را دیدیم. در قسمت بعدی، میخواهیم نحوه محافظت از مقادیر حساس پیکربندی را یاد بگیریم.
شما میتوانید دیگر قسمتهای این سری از آموزش سریالی را از صفحه ASP.NET Core Web API پیدا کنید.