1
votes

As with others, I disagree with injecting IOptions into every service that may need it, and looked for other options. My goal was to inject the values needed by the service, and nothing more. I came up with the following, which is working and functional, but is it SOLID and maintainable? Seems too simple and effective, like I'm missing something.

Requirements:

  • load a custom json file
  • strongly typed
  • not expose more than necessary
  • maintainable

I came up with the following.

public Startup(IConfiguration configuration)
{
     var configBuilder = new ConfigurationBuilder()
                  .SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                  .AddJsonFile("myappsettings.json", optional: false, reloadOnChange: true)
                  .AddEnvironmentVariables()
                  ;
                Configuration = configBuilder.Build();
}

Using LAMAR, so the following:

public void ConfigureContainer(ServiceRegistry services)
{
    var config = new MyAppConfig();
    Configuration.GetSection("MyAppConfig").Bind(config);
}

My json file looks like this:

{
  "MyAppConfig": {
    "AppSettings": {
      "Name": "My Application"
    },
    "DataSettings": {
      "PrimaryDB": {
        "Name": "DBNAME",
        "ConnectionString": "Server=serverAddr.DOMAIN.com;Database=INITIAL_DB_HERE;User Id=MY_APP_USER_NAME;Password=******;MultipleActiveResultSets=true",
        "DatabaseName": "DBNAME",  // this has a separate use
        "Provider": ""
      }
    }
  }
}

To use this, I'm simply passing the config object to the function that creates the service instance, and injecting to the service only what's needed.

services.For<IPerformanceService>().Use(c => CreatePerformanceService(config));

private IPerformanceService CreatePerformanceService(IAppConfiguration appConfig)
{
    var myRepo = new PerformanceRepository(appConfig.DataSettings.PrimaryDB.ConnectionString, appConfig.DataSettings.PrimaryDB.DatabaseName);

    return new PerformanceService(myRepo);
}
1

1 Answers

0
votes

I don't think this one has a definite answer, its more about what you think is convinient for you. For myself I went a slightly different approach.

This is startup:

// Redis Caching
var redisConfiguration = configuration.GetSection("Redis").Get<RedisConfiguration>();
services.AddSingleton(redisConfiguration);
services.AddSingleton<RedisCacheHelper>();

That part is pretty much what you are doing, a class to hold the config values. Note that you don't need "bind" you can directly use .Get to parse the config into a class.

I add my config into a singleton so i can use the regular DI to inject it into the constructor wherever i need it. In this case its only used for the CacheHelper but i have other places where a config is used by multiple controllers and middlewares.

Using the config is only the internal DI then:

public class RedisCacheHelper
{
    public StackExchangeRedisCacheClient Cache {get;set;}

    public RedisCacheHelper(RedisConfiguration redisconfig)
    {
        Cache = new StackExchangeRedisCacheClient(new NewtonsoftSerializer(),redisconfig );
    }

}