Dotnet Core Configuration Ayarları

1 Eyl

ASP.NET Core’da (6.0 versiyonda) bir uygulamaya dışarıdan bir konfigürasyon ayarı, bir veya daha fazla configuration provider kullanılarak gerçekleştirilebilir. Provider diye adlandırılan araçlar, appsettings.json dosyaları veya environment variables gibi çeşitli yapılandırma kaynaklarını kullanarak konfigürasyon ayar verilerini okurlar.

Uygulamada Configuration ayarları, Program.cs dosyası içerisinde yapılabilir. Ayarların değerleri ise appsettings.json dosyası içerisinde bulunur. Örneğin aşağıdaki appsettings.json içerisinde Connection isimli bir section oluşturulmuştur.

{
  "Connection": {
    "Host": "appserver.com",
    "Port": "1524"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Connection ayarlarını uygulama içerisinde kullanmanın birden fazla yolu vardır. Bunlar aşağıda başlıklar halinde belirtilmiştir.

IConfiguration interface ile erişim

Bir class içerisinden Connection section ayarlarına erişmek için IConfiguration interface kullanılabilir. Bu interface, Microsoft.Extensions.Configuration içerisinde bulunur ve dependency injection ile istenen class içerisinden erişilebilir.

public class DatabaseService
{
  private readonly string _host;
  private readonly int _port;

  public DatabaseService(IConfiguration config)
  {
    _redisHost = config["Connection:Host"];
    _redisPort = Convert.ToInt32(config["Connection:Port"]);
  }
}

Ancak bu yöntem ile her Configuration ihtiyacı sırasında, class içerisinde section isimleri, değer isimleri ve casting işlemleri yapılmak zorundadır.

İşi bir adım daha ileriye taşıyabilmek adına, ilgili configuration section, bir nesneye dönüştürülebilir.

ConnectionOptions class:

public class ConnectionOptions
{
    public const string SectionName = "Connection";

    public string Host{ get; set; } = String.Empty;
    public string Port { get; set; } = String.Empty;
}

Options pattern ile erişim

Configuration için bir nesne tanımlandıktan sonra, ilgili class içerisinde kullanım şekli aşağıdaki gibi değiştirilebilir.

public class DatabaseService
{
  private readonly ConnectionOptions options;

  public DatabaseService(IConfiguration config)
  {
     options = new ConnectionOptions();
     config.GetSection(ConnectionOptions.SectionName)
           .Bind(options);
  }
}

Alternatif bir yol olarak ise aşağıdaki kullanım da tercih edilebilir.

public class DatabaseService
{
  private readonly ConnectionOptions options;

  public DatabaseService(IConfiguration config)
  {
     options = config.GetSection(ConnectionOptions.SectionName)
                  .Get<ConnectionOptions>();
  }
}

Options pattern kullanırken en yaygın yöntem, servis binding işlemlerinin kullanıcı class içerisinde değil de aşağıdaki gibi Program.cs içerisinde yapılmasıdır.

Options pattern IOptions<T> interface ile erişim

Configuration binding ayarlarını, tek bir extension method içerisine alarak Program.cs içeriği sadeleştirilebilir. Bu amaçla, aşağıda bir extension method tanımlanmıştır.

    public static class ConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, 
             IConfiguration config)
        {
             services.Configure<ConnectionOptions>(
                 config.GetSection(
                     ConnectionOptions.SectionName));

            return services;
        }
    }

Program.cs aşağıdaki gibi düzenlenebilir.

using ConfigSample.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddConfig(builder.Configuration);

var app = builder.Build();

Bu durumda DatabaseService class içeriği aşağıdaki şekilde değiştirilebilir.

public class DatabaseService
{
  private readonly ConnectionOptions options;

  public DatabaseService(IOptions<ConnectionOptions> options)
  {
     options = options.Value;
  }
}

Configuration section class’a dependency injection ile erişim

Bir Microsoft extension tipi olan IOptions<T> tipini kullanmadan, DatabaseService içerisine doğrudan ConnectionOptions tipi dependency injection yöntemi ile verilebilir. Bunun için öncelikle ConfigServiceCollectionExtensions sınıfı içerisinde küçük bir değişiklik yapılmalıdır. İki farklı alternatif ile singleton olarak ConnectionOptions tipine binding sağlayabiliriz.

ConfigServiceCollectionExtensions Seçenek-1

    public static class ConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, 
             IConfiguration config)
        {
             services.Configure<ConnectionOptions>(
                 config.GetSection(
                     ConnectionOptions.SectionName));
    
             services.AddSingleton(options => 
                 options => 
                 GetService<IOptions<ConnectionOptions>>().Value);

            return services;
        }
    }

ConfigServiceCollectionExtensions Seçenek-2

    public static class ConfigServiceCollectionExtensions
    {
        public static IServiceCollection AddConfig(
             this IServiceCollection services, 
             IConfiguration config)
        {
             ConnectionOptions options = new();

             config.GetSection(ConnectionOptions.SectionName)
                     .Bind(options);
    
             services.AddSingleton(options);

            return services;
        }
    }

Bu durumda, DatabaseService içeriği aşağıdaki gibi yeniden düzenlenebilir. (Program.cs de hiç bir değişikliğe gerek yoktur, yukarıdaki ayarlar geçerlidir.)

public class DatabaseService
{
  private readonly ConnectionOptions options;

  public DatabaseService(ConnectionOptions options)
  {
     options = options;
  }
}

Artık servis sınıfı içerisinde Microsoft options pattern tipleri yerine, kendi sınıflarımızı kullanabilir hale getirmiş olduk.