configuration.getValue or configuration.getsection always returns null
Solution 1
When hosting in-process inside of IIS (or IIS Express), Directory.GetCurrentDirectory()
will return a different path to that which is returned when running out-of-process. Up until ASP.NET Core 2.1, IIS-based hosting was always out-of-process, but ASP.NET Core 2.2 introduces the ability to run in-process (which is the default when creating a new project).
When running out-of-process, Directory.GetCurrentDirectory()
will return the path to your ASP.NET Core application itself, whereas when running in-process, it will return the path to IIS (or IIS Express, e.g. "C:\Program Files\IIS Express").
From your question, the relevant code is this:
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
})
Here, before you make a call to SetBasePath
, the IConfigurationBuilder
has already been set up to use the correct path. Your call itself is overriding this path, setting it to e.g. "C:\Program Files\IIS Express". With this overridden base-path, your appsettings.json
et al files are no longer found, as they do not live in e.g. "C:\Program Files\IIS Express", and so no configuration is loaded from these files.
The solution is simply to remove your call to ConfigureAppConfiguration
so that the base-path does not get overridden. I realise you've already discovered this, but I wanted to make sure you had an explanation as to what was going wrong here.
Solution 2
Accessing the configuration in the Controller works a little different than in the Startup.cs. I did this a while back, just follow these steps:
Nuget: Microsoft.Extensions.Configuration.Binder
Put all the Configuration you want to access in your Controller into one Section, e.g. "EmailSettings":
appsettings.json
{
"EmailSettings": {
"EmailTemplatesPath": "EmailTemplates",
"Email": "[email protected]"
}
}
Then Create a Class in your Web API Project called EmailSettings.cs
:
EmailSettings.cs
public class EmailSettings
{
public string EmailTemplatesPath { get; set; }
public string Email { get; set; }
}
Then bind your Config values to an Instance of the EmailSettings
Class in Startup.cs
and add that Object to the Dependency Injection Container:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
EmailSettings emailSettings = new EmailSettings();
Configuration.GetSection("EmailSettings").Bind(emailSettings);
services.AddSingleton(emailSettings);
}
Now you can request your Configuration in the Api Controller by simply adding it to the Constructor like this:
Web API Controller
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
EmailSettings _emailSettings;
public ValuesController(EmailSettings emailSettings)
{
_emailSettings = emailSettings;
}
....
}
Just tried this again in my current Project (.NET Core 2.2 Web Api) and it worked. I put a Breakpoint in the ValuesController Constructor and the _emailSettings Object contained the values from the appsettings.json
file. You should be able to just Copy & Paste this! Have fun! :)
Solution 3
You made a simple mistake and forgot to add "Email" into "EmailSettings". Update your json as shown below and get the Email with config.GetSection("EmailSettings")["Email"];
"EmailSettings": {
"EmailTemplatesPath": "EmailTemplates",
"Email": "[email protected]"
},
Hope this solves your problem.
Edit:
If you wanna get those values from appsettings to anything other than startup, you should load those config values into a settings class and then inject appropiate IOptions instance to the method constructor in which you want to use those settings. To do so, please see my answer here.
Solution 4
While others have solved this problem, if you want to leave your startup services code as is, just change your Web API controller to the following:
private readonly EmailSettings _emailSettings;
public EmailController(IOptions<EmailSettings> emailSettings)
{
_emailSettings = emailSettings.Value;
}
The emphasis is on the .Value. That's why your code is returning null. I would also suggest changing program.cs back to it's default. And to use this in an action method, just do the following:
_email.settings.EmailTemplatesPath
One last thing - make sure your EmailSettings class structure is the exact same as your json.
Solution 5
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
Startup.cs:
public partial class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
app.UseMvcWithDefaultRoute();
}
}
Controller Class
You need to use Bind() method to get the value from section.
EmailSetting option = new EmailSetting();
//You need to bind the section with data model,
//it will automatically map config key to property of model having same name
config.GetSection("EmailSettings").Bind(option);
//Alternative to this you can read nested key using below syntax
var emailTemplatesPath = config["EmailSettings:EmailTemplatesPath"];
var emailInEmailSettings = config["EmailSettings:Email"];
// If email key is not nested then you can access it as below
var email = config.GetValue<string>("EmailOutside");
Email Setting Model:- (Property name should match with config Key)
public class EmailSetting
{
public string EmailTemplatesPath { get; set; }
public string Email { get; set; }
}
Appsetting.json:-
{
"AllowedHosts": "*",
"EmailSettings": {
"EmailTemplatesPath": "EmailTemplates",
"Email": "[email protected]"
},
"EmailOutside": "[email protected]"
}
Naveed Butt
Experienced coder, debugger and problem solver. **#SOreadytohelp**
Updated on June 22, 2022Comments
-
Naveed Butt almost 2 years
I am new to .Net Core and trying to get a value from the
appsettings.json
file but I have missed something very basic. Please let me know what I have done wrong... Here is the code...Program.cs
WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(Directory.GetCurrentDirectory()); })
Startup.cs
public IConfiguration Configuration { get; private set; } public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings")); }
Web API Controller
private readonly IConfiguration config; public EmailController(IConfiguration configuration) { if (configuration != null) { config = configuration; } }
Action Method
var emailTemplatesRelativePath = config.GetSection("EmailSettings"); var email = config.GetValue<string>("Email");
Both the above lines are returning null values for both
GetSection
andGetValue
appsettings.json
{ "Logging": { "LogLevel": { "Default": "Trace", "Microsoft": "Information" } }, "ConnectionStrings": { "FCRContext": "server=xxx;database=xxx;user id=xxx;password=xxx" }, "AllowedHosts": "*", "EmailSettings": { "EmailTemplatesPath": "EmailTemplates" }, "Email": "[email protected]" }
-
Felix over 5 yearsYou usually initialize IConfiguration in
Startup.cs
; not inProgram.cs
. If you are getting null the way you describe, something is wrong there. Please showStartup.cs
-
Naveed Butt over 5 yearsdocs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/… I am following this. I tried Options Pattern as well, but it didn't work...
-
Naveed Butt over 5 years.Net Core 2.2 and using IIS Express for now..
-
-
Naveed Butt over 5 yearsThose are two separate statements suggesting that none of them are returning any value.
-
Naveed Butt over 5 yearsNo, The two code lines are there to show that both the
GetSection
and theGetValue
are returning no values. -
Naveed Butt over 5 yearsThis
config["EmailSettings:EmailTemplatesPath"]
still returnsnull
. -
Naveed Butt over 5 yearsand according to this docs.microsoft.com/en-us/dotnet/api/… Bind is not available in
.Net Core 2.2
-
Ashish Mishra over 5 yearsPlease try to create simple MVC project with one controller to read config file. I have updated answer and added the code sample in it. try to keep this dummy project very simple, do not add any middle ware in pipeline except MVC.
-
Naveed Butt over 5 yearsThanks. That helped. I created an empty application and that was returning the configuration values correctly. Then I compared the two
program.cs
, the problem was this line that I had to .ConfigureAppConfiguration((hostingContext, { config.SetBasePath(Directory.GetCurrentDirectory()); }) -
Naveed Butt over 5 yearsDon't know why that is the problem. The MSDN says that I need to add that... docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/…
-
Robert Bantele over 5 yearsUpdated to work in Controllers. I tested it myself, just give it a try :)
-
Ashish Mishra over 5 yearsI tried with and without ConfigureAppConfiguration().In both cases code were working fine.
-
Hasan over 5 yearsCan you see my answer on this link and get those values from appsetting.json via settings class and inject it to the controller with IOptions. stackoverflow.com/a/53904527/5198054
-
Ensunder about 5 yearsAre there any implications when deploying this to 1) older versions of IIS or 2) latest versions of IIS? If we have "in-process" set for our project, do we just never use config.SetBasePath even though its in the project when it's created? If so, I guess its just MS hasn't updated the template for it.