65

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

6
  • 1
    stackoverflow.com/questions/38572081/… did you see this? Commented Jan 20, 2017 at 20:41
  • 8
    @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(); Commented Jan 20, 2017 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? Commented Jan 20, 2017 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. Commented Jan 20, 2017 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. Commented Jan 20, 2017 at 22:41

5 Answers 5

95

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.

Sign up to request clarification or add additional context in comments.

11 Comments

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.
+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.
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;
Great answer, one of the situations where this can be useful is in an integration test where dependency injection is not an option.
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<string>(...)
|
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.

2 Comments

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.
@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.
7

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.

1 Comment

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.
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

}

2 Comments

Intrsting approach I am trying to do it with sql EF Config store any ideas how one would adapt when not using the json file
What is EF Config store? how dow you implement it?
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.