appsettings.json file not in .net core console project - asp.net-core

I understand that .net core has replaced the app.config file with appsetting.json. However this file seems to be added for ASP.net projects only. In fact it is not even available in the add items list.
I found this post that list packages needed to be added:
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Json
I added all these and it does give me the option of adding a json configuration file but still not the App Settings File which is only available under ASP.Net Core.
I am trying to understand why, doesn't a non web project need configuration and what is the recommended way to configure a .net core console application.
Thanks in advance.

Non-web project may or may not need configuration. But, as you noticed, Visual Studio doesn't scaffold console projects with appsettings.json. Obviously, you can add it to the project as json file. Once you have it, the challenge is to make use of it. I frequently use Configuration object and dependency injection in Entity Framework utilities.
For example,
public static class Program
{
private static IConfigurationRoot Configuration { get; set; }
public static void Main()
{
IConfigurationBuilder builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
IServiceCollection services = new ServiceCollection();
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IMyService, MyService>();
IServiceProvider provider = services.BuildServiceProvider();
IMyService myService = provider.GetService<IMyService>();
myService.SomeMethod();
}
public class TemporaryDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
IConfigurationBuilder configBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
IConfigurationRoot configuration = configBuilder.Build();
DbContextOptionsBuilder<MyDbContext> builder = new DbContextOptionsBuilder<MyDbContext>();
builder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
return new MyDbContext(builder.Options);
}
}
}
This allows me to both run migrations and console-based utilities against DbContext. You don't specify what kind of configuration you are going to need - so this is just one example. But hopefully, you can adjust it to your needs.

Related

Design Time DbContext Factory - Class Library how to get connection string from UserSecrets in a class library project?

I have separated my Database project as a class library. And I am defining a IDesignTimeDbContextFactory implementation. I want to use user-secrets to keep the config settings. I get an error as follows. How can i make this setup work?
Note: I have already setup the user-secrets for the class library project. And my user secrets has the necessary values:
{
"ConnectionStrings:CatalogDbContext": "Server=(localdb)\\\\mssqllocaldb;Database=Catalog;Trusted_Connection=True;MultipleActiveResultSets=true"
}
Project Structure:
Catalog
|---API(Web API)
|---Data(Class Lib)
|- appsettings.json
|- IDesignTimeDbContextFactory<CatalogDbContext>
Appsettings.json
{
"ConnectionStrings": {
"CatalogDbContext": ""
}
}
public class CatalogDbContextFactory : IDesignTimeDbContextFactory<CatalogDbContext>
{
public CatalogDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var dbContextOptionsBuilder = new DbContextOptionsBuilder<CatalogDbContext>();
var connectionString = configuration.GetConnectionString(nameof(CatalogDbContext));
dbContextOptionsBuilder
.UseSqlServer(connectionString);
return new CatalogDbContext(dbContextOptionsBuilder.Options);
}
}
This command fails with an error:
Catalog.Api> dotnet ef migrations add Catalog.Initial --project ..\Catalog.Data\Catalog.Data.csproj --verbose
Finding DbContext classes in the project...
Found DbContext 'CatalogDbContext'.
Using DbContext factory 'CatalogDbContextFactory'.
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.ArgumentNullException: Value cannot be null. (Parameter 'connectionString')
at Microsoft.EntityFrameworkCore.Utilities.Check.NotEmpty(String value, String parameterName)
at Microsoft.EntityFrameworkCore.SqlServerDbContextOptionsExtensions.UseSqlServer(DbContextOptionsBuilder optionsBuilder, String connectionString, Action`1 sqlServerOptionsAction)
I can clearly say this is because the connectionstring is empty. The user secrets configuration provided is not added to the list of providers. I don't know what the setup will look like to add user secrets to a class library project. Please help!
I tried as you mentioned and repeated your error in my case
then I modified the appsettings.json in webapi project and added the migration successfully this time,it still read the jsonfile in your webapi project

aspnet core dependency injection with IOptions in startup

What is going on gang?
I am looking for a proper and clean way of injecting configuration into my classes using the IOptions package. Currently, I am registering all the configuration object in one place and everything is just fine. The problem is with some classes that I need to register in the startup method.
The initial method implementation looks like this: (extension method)
public static void RegisterHttpClients(this IServiceCollection services, AppSettings appSettings)
{
services.AddHttpClient<IPdfService, PdfService>(
httpClient => httpClient.DefaultRequestHeaders.Add("PdfApiKey", appSettings.PdfApiKey));
}
This AppSettings object I am also injecting into other classes using IOptions so would like to somehow reuse the object from there instead of having to get same object from json again like that:
var appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>();
services.RegisterHttpClients(appSettings);
In other words - I already have the thing registered with services.Configure<AppSettings>(configuration.GetSection("AppSettings")); so would kind of like to piggy-back on this if possible.
Any thoughts/suggestions for possible directions on how to configure this?
EDIT: (added JSON and AppSettings files)
public class AppSettings
{
public string PdfApiKey {get; set;}
public string AnotherProperty {get; set;}
}
{
"AppSettings": {
"PdfApiKey": "SomeKey",
"AnotherProperty": "HollyTheCow"
}
}
Thanks in advance!
You can accomplish this by registering your options with Configure (which you've done), then using the second overload to AddHttpClient() to get the registered options from the DI container (Service Locator pattern). Note I've used IServiceProvider.GetRequiredService, but you can use IServiceProvider.GetService()?.Value if it's an optional setting.
services.Configure<AppSettings>(_configuration.GetSection("AppSettings"));
services.AddHttpClient<YourService>((serviceProvider, config) => {
var settings = serviceProvider.GetRequiredService<IOptions<AppSettings>>().Value;
config.BaseAddress = new Uri(settings.BaseUri);
});
Your code has no any error and will run successfully
There may be many appSetting.json files on your project.
Check the ASPNETCORE_ENVIRONMENTvariable on your project.
Right click on your project > Properties > Debug > Environment variables
if ASPNETCORE_ENVIRONMENT is set on Development IConfiguration read data from
appSettings.development.json and you should add AppSettigs json in appSettings.development.json
if ASPNETCORE_ENVIRONMENT is set on Production IConfiguration read data from
appSettings.production.json and you should add AppSettigs json in appSettings.production.json
if you want IConfiguration read data from appSetting.json delete both appSettings.development.json and appSettings.production.json file.
in this case IConfiguration by default read and bind data from appSetting.json
Another options
You can config your IConfiguration to load data dynamically from
appSettings.json like this
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.configuration = builder.Build();
}

How to set ASPNETCORE_ENVIRONMENT when testing .NET Core 2.0 class library with xUnit in VS2017

I use this code to load settings depending on Environment in my xUnit test project:
public class TestSettings
{
public string AzureConnectionString { get; }
public TestSettings(ITestOutputHelper output)
{
string envVariable = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
output.WriteLine($"env: '{envVariable}'");
IConfigurationRoot config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{envVariable}.json", optional: true)
.Build();
AzureConnectionString = config.GetConnectionString("AzureStorage");
}
}
But whatever I do I get envVariable empty. Where can I set ASPNETCORE_ENVIRONMENT for test project (VS2017)?
To ASP.NET Core Tooling Team
Please make test project settings work for environment. Thank you.
ok, on Windows set ASPNETCORE_ENVIRONMENT with this command:
setx ASPNETCORE_ENVIRONMENT Development
so the value stays (notice the 'setx').
Also, look at this answer https://stackoverflow.com/a/43951218/2896495.

How to configure Kestrel urls in startup class

I am trying to figure out the proper way to modify the URL(s) that kestrel listens on from the Startup class constructor.
Update: In addition to the answer below, I've come to understand that Startup class isn't used to configure Kestrel in the way I had thought. I had thought that Startup would create a single application-wide configuration object that would be located through convention, it is not. As #Tseng points out, the configuration of the Application and Hosting are separate concerns. The linked answer and accepted answer provide working examples.
I spun up a brand new Ubuntu 14 box in Vagrant and installed ASP.NET Core 1.1 according to the current instructions from Microsoft: https://www.microsoft.com/net/core#linuxubuntu
I ran:
dotnet new -t web
dotnet restore
dotnet run
This listens on http://localhost:5000 by default. In Program.cs I can call app.UseUrls("http://*:5001) and this works to change the URL and port.
What I really want is to change the URLs by convention through adding a new JSON setting file in the Startup class, so I followed examples and created a hosting.JSON file with this (note: I've tried "server.urls" and "urls" as the key).
{
urls: "http://*:5001"
}
In Startup.cs below the default lines for adding the appsettings.json files I added .AddJsonFile("hosting.json", optional: false); (optional: false to make sure it was picking up the file)
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("hosting.json", optional: false);
if (env.IsDevelopment())
{
// For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
I have verified that the setting is there after the configuration builds, but it is not picked up or used when the host is built in the Program.cs
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
and the application still starts listening on localhost:5000. It is my (likely incorrect) understanding that by having a setting properly named with the correct key the WebHostBuidler should pick it up and use it.
I've seen other examples that basically get rid of the startup class and create the configuration in Program.cs where it can then be passed into a call to UseConfiguration but again my understanding is that using a Startup class should do this by convention.
Essentially I'd like to keep Startup and Program separate, add a hosting.JSON file to the configuration with URLs, and have it picked up and used without needing to call UseUrls, UseConfiguration, etc.
Am I missing something obvious, or trying to do something that is not actually correct?
To explain why this is not a duplicate as per my comment:
That post specifically says "launcherSettings is for Visual Studio F5". I am using the command line on a Linux box. Nothing to do with VS.
The solution provided in that post moved the configuration build into the main method. I specifically state that I want to build my configuration in the Startup class just like the default "dotnet new -t web" project.
I do not feel this is a duplicate, and am still reviewing the ASP.NET Core source to see if this is possible.
In regards to the correct key:
https://github.com/aspnet/Hosting/blob/b6da89f54cff11474f17486cdc55c2f21f2bbd6b/src/Microsoft.AspNetCore.Hosting.Abstractions/WebHostDefaults.cs
namespace Microsoft.AspNetCore.Hosting
{
public static class WebHostDefaults
{
public static readonly string ApplicationKey = "applicationName";
public static readonly string StartupAssemblyKey = "startupAssembly";
public static readonly string DetailedErrorsKey = "detailedErrors";
public static readonly string EnvironmentKey = "environment";
public static readonly string WebRootKey = "webroot";
public static readonly string CaptureStartupErrorsKey = "captureStartupErrors";
public static readonly string ServerUrlsKey = "urls";
public static readonly string ContentRootKey = "contentRoot";
}
}
https://github.com/aspnet/Hosting/blob/80ae7f056c08b740820ee42a7df9eae34541e49e/src/Microsoft.AspNetCore.Hosting/Internal/WebHost.cs
public class WebHost : IWebHost
{
private static readonly string DeprecatedServerUrlsKey = "server.urls";
In your hosting.json file you should use server.urls instead of urls. And hosting.json file need to added in the program.cs (in the main method) not in startup.
Here is my hosting.json file.
{
"server.urls": "http://localhost:5010;http://localhost:5012"
}
Here is the Main method.
public static void Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddCommandLine(args)
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
.AddJsonFile("hosting.json", optional: true)
.Build();
var host = new WebHostBuilder()
.UseConfiguration(config)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
And here is the screenshot.

ASP.NET Core ignores ASPNET_ENV and Hosting:Environment

No matter when or where I set either ASPNET_ENV or Hosting:Environment the startup code will always enter
//This method is invoked when ASPNET_ENV is 'Production'
//The allowed values are Development,Staging and Production
public void ConfigureProduction(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(minLevel: LogLevel.Warning);
Configure(app);
}
What I've tried so far
Set Hosting:Environment to Development in the project properties
Set ASPNET_ENV to Development in the project properties
Set Hosting:Environment to Development in launchSettings.json
Set ASPNET_ENV to Development in launchSettings.json
Set ASPNET_ENV to Development in code via Environment.SetEnvironmentVariable("ASPNET_ENV", "Development"); in the Startup method before the call to ConfigurationBuilder.GetEnvironmentVariables()
This is version 1.0.0-rc2-20143 by the way. Am I missing something here or is it just a bug?
The environment variable name has been changed to ASPNETCORE_ENVIRONMENT as part of the name change.
It was announced in this issue and change in this PR.
If you have not done it already, try adding the environment variables to the configuration instance.
public static void Main(string[] args)
{
var builder = new ConfigurationBuilder();
builder.AddJsonFile("appsettings.json");
builder.AddEnvironmentVariables();
var config = builder.Build();
}
Whilst the answer from Henk Mollema is correct for the environment variable name, I still encountered issues where dotnetcore would seem to ignore the environment variable and use appsettings.json when run from command line.
To ensure it uses the correct environment and app settings, try the following:
Change the environment variable via Project Properties -> Debug -> Environment Variables -> ASPNETCORE_ENVIRONMENT -> Value : Change the value to match your intended environment eg Staging
Build Solution
From commandline, type SETX ASPNETCORE_ENVIRONMENT "YOUR-ENVIRONMENT-NAME" where replace "YOUR-ENVIRONMENT-NAME" to match what you set in step 1 eg Staging and ensure you include the "quotes"
IMPORTANT - make sure you all your various appsettings.json eg appsettings.staging.json is present in the project directory where you will run it. (In the case of a published solution, the appsettings.staging.json may not have been copied, so ensure it's there)
From Commandline, go to your project directory (or published directory) and run your project by typing dotnet run (or "dotnet YOURPROJECTNAME.DLL" for published projects)
Observe in the next line that appears in the commandline which states Hosting environment: YOURENVIRONMENTNAME eg Hosting environment:staging
Running a DotNetCore project from Visual Studio always picked the
correct appsettings based on the environment set in your project
properties, however these are the steps I followed to run dotnet core correctly from commandline
working correctly.
This worked for me using .net core 2.0 and building a web api. Notice the 2 different methods of accessing the environment variable.
Program.cs
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args){
if(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT ") == "Production"){
return WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
} else {
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
But in the startup file there is another environment option
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()));
var connectionString = "Data Source=tcp:<some-ip>,<some-port>;Initial Catalog=<some database>;Integrated Security=False;User Id=SA;Password=<some password>;MultipleActiveResultSets=True";
services.AddDbContext<myContext>(opt => opt.UseSqlServer(connectionString));
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("AllowAll");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}