62

I was wondering if there was a way to access Configuration (Microsoft.Extensions.Configuration) without the use of dependency injection. Only examples I see are through constructor injection (using IOptions or injecting Configuration directly).

My dilemma is that I have a utility class-- not a service-- that has static methods to do things on the fly. In a few of those static methods I would like to retrieve a couple of properties from appsettings.json dynamically. Since this is strictly a utility class, I don't want have to inject this class into every other class that needs to use a method or two from the utility.

Any ideas on how to access the properties of appsettings.json without some sort of dependency injection.

FYI: using c# and .net core 1.1

Los Morales
  • 2,061
  • 7
  • 26
  • 42
  • 1
    http://stackoverflow.com/questions/38572081/access-app-key-data-from-class-libraries-in-net-core-asp-net-core did you see this? – DanielGatti Jan 20 '17 at 20:41
  • 6
    @DanielGatti The answers use injection. Not what I'm looking for. Again it's for my utility class. It is not a service, does not have an interface, should not be injected. All you do to use it is this: UtilityClass.DoSomething(); – Los Morales Jan 20 '17 at 20:45
  • Do the methods need to be static? Can each class that needs to use Utility just have a private member of the Utility type that gets set/created when the class object is constructed? – BackDoorNoBaby Jan 20 '17 at 21:40
  • @BackDoorNoBaby So are you suggesting to inject the utility? To answer your questions though, no on the 1st (although preferred), depends on the 2nd. – Los Morales Jan 20 '17 at 22:12
  • I've not gone looking for this particular configuration file before so forgive me if I misunderstand the context. I'm assuming the problem is that you want to store a reference to the retrieved configuration, rather than run it through ConfigurationBuilder each time? You could swap your static utils for a singleton pattern which would allow you to do this (paying attention to thread safety). That said, for me i think i'd try and inject it somehow to remove the dependency. – Christopher Thomas Jan 20 '17 at 22:41
  • @ChristopherThomas Any way to do this in core? ConfigurationManager.AppSettings["MySetting"] – Los Morales Jan 21 '17 at 02:21

5 Answers5

84

Wow, what a lot of comments, why don't people answer the question instead of telling someone they don't want to do what they obviously do. Anyway, hopefully this will keep both camps satisfied.

If you take a standard AppSettings class with a single public constructor that takes an IConfiguration that can be used to populate all the AppSettings properties, this keeps the ability for Dependency Injection.

If at the end of the constructor we set a static property 'Current' pointing to the current instance of AppSettings, this will allow us access to the settings from that point onwards via the static property without the need for further injection.

If we now create a static Default 'GetCurrentSettings' method to get the settings from a json file, this can be used as a default instantiation, so that if 'Current' is called and is set to null, we just go off and populate the settings from the file. Here's an example of what I mean...

public class AppSettings
{
    private static AppSettings _appSettings;

    public string AppConnection { get; set; }

    public AppSettings(IConfiguration config)
    {
        this.AppConnection = config.GetValue<string>("AppConnection");

        // Now set Current
        _appSettings = this;
    }

    public static AppSettings Current
    {
        get
        {
            if(_appSettings == null)
            {
                _appSettings = GetCurrentSettings();
            }

            return _appSettings;
        }
    }

    public static AppSettings GetCurrentSettings()
    {
        var builder = new ConfigurationBuilder()
                        .SetBasePath(Directory.GetCurrentDirectory())
                        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                        .AddEnvironmentVariables();

        IConfigurationRoot configuration = builder.Build();

        var settings = new AppSettings(configuration.GetSection("AppSettings"));

        return settings;
    }
}

So from this you would be able to call anywhere in code AppSettings.Current.AppConnection

If it's been instantiated using DI the injected version would be retrieved otherwise the default version would be taken from an appsettings.json file. I doubt it satisfies everyone and I'm not sure I've explained it very well, but hopefully it makes sense.

Hoots
  • 1,876
  • 14
  • 23
  • 58
    Plus 1 for "why don't people just answer the question". I'm so sick of being told I should not do what I'm asking how to do. – ProfK Mar 09 '18 at 11:42
  • 3
    +1 for the same reason as @ProfK said. I'm sure they are trying to help you to better code style but I do feel your pain. It's a bit insulting that everybody assumes that you're dumb and need to be told what you want to ask. (In some caes it **actually is** so but it's insulting never the less.) They should state the answer to **your** question and then offer an alternative. Or at least explain that they're not answering your question and inquire whether you'd like to see an alternative approach. On the other hand, SO is free so you get more than you paid for anyway, hehe. – DonkeyBanana Apr 22 '18 at 18:07
  • 7
    I just wanted to add that for newer versions of .NET Core, you will need to include the following packages: `Microsoft.Extensions.Configuration;` `Microsoft.Extensions.Configuration.Json;` `Microsoft.Extensions.Configuration.EnvironmentalVariables;` `Microsoft.Extensions.Configuration.FileExtensions;` – Propagating Aug 16 '18 at 02:31
  • 1
    To get more details for @Propagating comment, and to fix the error in this code, [this](https://stackoverflow.com/questions/46843367/how-to-setbasepath-in-configurationbuilder-in-core-2-0) and [this](https://stackoverflow.com/questions/54767718/iconfiguration-does-not-contain-a-definition-for-getvalue) SO questions are really helpful. – Endri Nov 20 '19 at 15:59
  • 2
    Great answer, one of the situations where this can be useful is in an integration test where dependency injection is not an option. – Bunnynut Oct 07 '20 at 08:24
  • @Hoots is it possible to get the settings based on environment? – Endri Nov 17 '20 at 17:50
  • @Endri, in the example given on the ConfigurationBuilder, that’s what the AddEnvirinmentVariables is to include those in the Configuration. – Hoots Nov 18 '20 at 19:47
  • Simple and fair. Thanks for the answer. It helped me – no-stale-reads Dec 09 '20 at 16:38
  • 1
    In addition to the other packages listed in a comment above, the package `Microsoft.Extensions.Configuration.Binder` is now required for the method `config.GetValue(...)` – TheAtomicOption Feb 22 '21 at 16:10
  • I like this, but wouldn't it be even simpler to just deserialize the JSON file directly? – Emperor Eto Oct 06 '21 at 18:45
13

I just made the config property in Startup static:

public static IConfiguration Configuration { get; set; }

Now I can access it anywhere in the application with just Startup.Configuration.

ProfK
  • 49,207
  • 121
  • 399
  • 775
  • 6
    Problem with this is that Startup is now exposed and a dependency for all those requiring Configuration "statically". IMO this is not ideal due to this being the Startup class-- not the intended purpose. A recent answer using a custom AppSettings class is more appropriate since it somewhat mimics the ConfigurationManager.AppSettings from pre-core days built specifically for serving up configuration. But yes, your solution will work if you're ok calling Startup.Configuration from anywhere in your code requiring it. – Los Morales Mar 09 '18 at 18:21
  • 1
    @LosMorales Yes but in an above answer using an `AppSettings`, the config is injected into that. If I'm going to inject config there, I might as well inject it wherever I need it, and that is my whole reason for this question. E.g. I use `var model = new ProductViewModel` and need an `IConfiguration` in that viewmodel, I can't use an injected parameter. – ProfK Mar 10 '18 at 03:42
5

I am totally agree with solution suggested by @Hoot but i have modified class little. i have modified class because i need to pass dynamic Key to fetch value for same..

Configuration Class :

public class ConfigHelper
    {

        private static ConfigHelper _appSettings;

        public string appSettingValue { get; set; }

        public static string AppSetting(string Key)
        {
          _appSettings = GetCurrentSettings(Key);
          return _appSettings.appSettingValue;
        }

        public ConfigHelper(IConfiguration config, string Key)
        {
            this.appSettingValue = config.GetValue<string>(Key);
        }

        public static ConfigHelper GetCurrentSettings(string Key)
        {
            var builder = new ConfigurationBuilder()
                            .SetBasePath(Directory.GetCurrentDirectory())
                            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                            .AddEnvironmentVariables();

            IConfigurationRoot configuration = builder.Build();

            var settings = new ConfigHelper(configuration.GetSection("AppSettings"), Key);

            return settings;
        }
    }

Appsettings.Json Configuration:

 "AppSettings": {
    "WebApplicationUrl": "http://localhost:0000/",
    "ServiceUrl": "http://localhost:0000/",
    "CommonServiceUrl": "http://localhost:0000/"
  }

Calling Example:

string ServiceUrl = ConfigHelper.AppSetting("ServiceUrl");

So from now we are able to pass dynamic key.

Ankit Mori
  • 705
  • 14
  • 23
  • This lacks the autocomplete feature which strongly typed properties provide. A better option is to duplicate this `public string AppConnection { get; set; }` line of code @Hoots answer for other config values you have in _appsettings.json_ such as _WebApplicationUrl_ or _ServiceUrl_ etc. – Endri Nov 20 '19 at 16:37
1

I have modified @Ankit Mori answer to a better dynamic solution and with strongly typed properties too.

using System.IO;
using Microsoft.Extensions.Configuration;

public class AppSettings
{

    #region Methods

    public static string GetSettingValue(string MainKey, string SubKey)
    {
        return Configuration.GetSection(MainKey).GetValue<string>(SubKey);
    }

    #endregion

    #region Properties

    public static IConfigurationRoot _configuration;
    public static IConfigurationRoot Configuration
    {
        get
        {
            if (_configuration == null)
            {
                IConfigurationBuilder builder = new ConfigurationBuilder()
                        .SetBasePath(Directory.GetCurrentDirectory())
                        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                        .AddEnvironmentVariables();
                _configuration = builder.Build();
            }
            return _configuration;
        }
    }
    public static string MainAPI
    {
        get
        {
            return Configuration.GetSection("AppSettings").GetValue<string>("AppConnection");
        }
    }

    #endregion

}
0

You can try a utility method like so:

private static T? GetServiceInstance<T>(IServiceCollection services) where T : class
{
    return (T?)services.Where(s => s.ImplementationInstance is T)
        .Select(s => s.ImplementationInstance)
        .FirstOrDefault();
}

Then call it like like so:

var configuration = GetServiceInstance&lt;IConfiguration>()

Should work for IConfiguration and IWebHostEnvironent because those services are created before the IServiceCollection.
Mike Russo
  • 111
  • 1
  • 2