.Net Core How to Access Configuration Anywhere in application

10,034

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.

Share:
10,034
mo_maat
Author by

mo_maat

Updated on June 05, 2022

Comments

  • mo_maat
    mo_maat almost 2 years

    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://docs.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.

  • mo_maat
    mo_maat over 5 years
    This seems to have worked, but my challenge/question is still the same. How do I call MyService? Do I have to pass it in a MyConfig object when instantiating it? I'm basically trying to call a method in MyService from within my startup class:
  • mo_maat
    mo_maat over 5 years
    I've edited my initial post to add the code in my startup class that is attempting to call the Something class which relies on dependency injection. I've made the changes you sggested @Alex Riabov and they work, but I need help figuring out how to call Something from within my startup class as outlined in the edits in my post.
  • Alex Riabov
    Alex Riabov over 5 years
    @mo_maat What do want to achieve? I'm not sure I understand that from your edit
  • Simply Ged
    Simply Ged over 5 years
    In your HandleGetData method you need to retrieve the class from the DI system. Replace the Something something = new Something(); line with var something = app.ApplicationServices.GetService<Something>();. The DI system will create the Something class and inject the IOptions<> service for you.
  • mo_maat
    mo_maat over 5 years
    @AlexRiabov @SimplyGed I've made changes to the post to show what my code looks like now with all the suggested edits. To sum up my problem, I would like to be able to call DataHelper from the HandleData function within my Startup class. DataHelper would then return a string (the idea ultimately is to return data from a database based on the connection string in appsettings). As it stands I'm getting the following error: `NullReferenceException: Object reference not set to an instance of an object.
  • galdin
    galdin over 5 years
    @mo_maat you should consider doing what Simply_Ged suggested. If you want the method to only be called once during application startup, consider doing it in Main() after building the webhost but before running it. You can get the service instance from the webhost object's DI container. (Also, since this answer addresses the original question, you should accept it :)
  • mo_maat
    mo_maat over 5 years
    I did try doing what SimplyGed suggested, but it is not working. I'll have to keep playing around with this.
  • Simply Ged
    Simply Ged over 5 years
    Based on your edited question you still need to register DataHelper and IDataHelper in ConfigureServices to be able to access it using GetService