Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net Core How to Access Configuration Anywhere in application

I have read through the documentation on the different ways to setup and access configuration in .Net Core 2.1 and also the options pattern that seems to be recommended (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1). However, I can't seem to get what I want working:

I have done the following:

AppSettings:

{
  "ConnectionStrings": {
    "DefaultConnStr": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=true",
    "AW2012ConnStr": "Server=localhost;Database=AW2012;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=true"
  }
}

MyConfig:

public class MyConfig
{
    public string AWConnStr { get; }
    public string DefaultConnStr { get; }
}

Startup:

public class Startup
{

public IConfiguration _config { get; set; }

public Startup(IHostingEnvironment env)
{
     var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
     _config = builder.Build();

}

public void ConfigureServices(IServiceCollection services)
{
      services.AddOptions();

      //add config to services for dependency injection
      //services.AddTransient<IMyConfig, MyConfig>();
     //services.AddScoped<IMyConfig, MyConfig>();
     var section = _config.GetSection("ConnectionStrings");
     services.Configure<MyConfig>(section);
}

    private static void HandleGetData(IApplicationBuilder app)
    {
        //DataHelper dataHelper = new DataHelper(_dataHelper);
        var _dataHelper = app.ApplicationServices.GetService<DataHelper>();

        app.Run(async context =>
        {
            //await context.Response.WriteAsync("<b>Get Data</b>");
            //await context.Response.WriteAsync(dataHelper.GetCompetitions(context.Request.QueryString.ToString()));
            await context.Response.WriteAsync(_dataHelper.GetCompetitions(context.Request.QueryString.ToString()));
        });
    }


  public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {

            app.Map("/Route1", HandleRoute1);

            app.Map("/Route2", HandleRoute2);

            app.Map("/GetData", HandleGetData);

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Non Mapped Default");
            });
        }
 }

I would like to then access the configuration in any class anywhere in my code. So for example I have the following class where I would like to just read the configuration information:

public interface IDataHelper
{
    string GetCompetitions(string val);
}

public class DataHelper : IDataHelper
{
    private readonly MyConfig _settings;

    public DataHelper(IOptions<MyConfig> options)
    {
        _settings = options.Value;
    }

    public string GetCompetitions( string queryStringVals)
    {

        return _settings.AWConnStr;

    } 
}

As shown above in my Startup class I then want to access/call something in the HandleGetData function in my startup, so that when I browse to the following route: http://localhost:xxxxx/getdata I get back the response from the Something.GetData function.

Is this correct? The problem I'm having is that when I create an instance of class Something, it is requiring me to pass in the configuration object, but doesn't that defeat the purpose of injecting it. How should I be setting this up to work similar to how DBContext gets the context injected with the configuration options. And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.

like image 763
mo_maat Avatar asked Dec 18 '22 21:12

mo_maat


2 Answers

I would say that in .Net Core application you shouldn't pass instance of IConfiguration to your controllers or other classes. You should use strongly typed settings injected through IOtions<T> instead. Applying it to your case, modify MyConfig class (also property names should match names in config, so you have to rename either config (DefaultConnection->DefaultConnStr, AW2012ConnStr->AWConnStr or properies vice versa):

public class MyConfig
{    
    public string AWConnStr { get; set; }
    public string DefaultConnStr { get; set; }
}

Register it:

public void ConfigureServices(IServiceCollection services)
{
    // in case config properties specified at root level of config file
    // services.Configure<MyConfig>(Configuration);

    // in case there are in some section (seems to be your case)
    var section = Configuration.GetSection("ConnectionStrings");
    services.Configure<MyConfig>(section);
}

Inject it to required service:

public class MyService
{
    private readonly MyConfig _settings;

    public MyService(IOptions<MyConfig> options)
    {
        _settings = options.Value;
    }
}

And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.

Transient lifetime services are created each time they're requested.

Scoped lifetime services are created once per request.

like image 89
Alex Riabov Avatar answered Feb 24 '23 12:02

Alex Riabov


You have to do the same thing for the Something as you did for MyConfig like:

public interface ISomething
{
    string GetSomeData();
}

Then:

public class Something : ISomething
{
    public IConfiguration _config { get; set; }

    public Something(IConfiguration configuration)
    {
        _config = configuration;
    }

    public string GetSomeData()
    {
        return _config["DefaultConnStr"];
    }
}

Then in the ConfigureService method of the Startup class as follows:

services.AddScoped<ISomething,Something>();

Then call the GetSomeData() as follows:

public class CallerClass
{
     public ISomething _something { get; set; }

     public CallerClass(ISomething something)
     {
            _something = something;
     }

     public string CallerMethod()
     {
         return _something.GetSomeData();
     }
}

Then:

And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.

Here is the details about this from microsoft:

Service Lifetime details in ASP.NET Core

like image 21
TanvirArjel Avatar answered Feb 24 '23 13:02

TanvirArjel