I want to use user secrets for KeyVault clientid etc. I call my configs from program.cs when I setup KeyVault. When I add the values in appsettings.json all works fine, but if I remove it and manage user secrets it's always say null when my program.cs is running.
And I have the correct values in the created secret.json and the attribute with a guid to it is also in my project file
I have also tried to add a key and value to the secret.json and tried to get it from a controller and it's also null.
So it seems like WebHost.CreateDefaultBuilder in my program.se does not load my secret.json file.
And if I add AssUserSecrets to my WebHost.CreateDefaultBuilder(args) in progam.cs there is no difference same problem.
I'm using WebHost.CreateDefaultBuilder so it shall be used by default in asp .net core 2.2
dontnet user-secret list
Will also shows the values for me from my secret.json file so the value is there, the user-secret has been initialized.
dontnet user-secret init
Also, say it's already setup.
But still just null when calling my secret.json attributes
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureServices(services => services.AddAutofac())
.ConfigureAppConfiguration((context, builder) =>
{
var config = builder.Build();
var vault = config["KeyVault:Vault"];
builder.AddAzureKeyVault($"https://{config["KeyVault:Vault"]}.vault.azure.net/", config["KeyVault:ClientId"], config["KeyVault:ClientSecret"]);
});
I test in my site and it works fine, here is the steps you could refer to.
1.Right click on the project and select Manage User Secrets. It will open the secrets.json file in Visual Studio where you can add your secrets and it will add the to the .csproj file.
2.Add what you may wish to add to your User Secrets. It includes two ways of writing a json object and a simple key/value pair.
3.Mapping User Secrets to a model.
public class KeyVault
{
public string Vault { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
4.Adding the Configuration.
Install package Microsoft.VisualStudio.Web.CodeGeneration.Design and Microsoft.Extensions.Configuration and Microsoft.Extensions.Configuration.UserSecrets
5.Bind the values. This is done in the Startup.cs file.
6.Using your mapped secrets
public class HomeController : Controller
{
private readonly KeyVault _keyvault;
// I’ve injected twilioAccountDetails into the constructor
public HomeController(IOptions<KeyVault> keyvault)
{
// We want to know if twilioAccountDetails is null so we throw an exception if it is
_keyvault = keyvault.Value ?? throw new ArgumentException(nameof(keyvault));
}
........
}
7.Call it in Program.cs.
For more details about how to use User Secrets in a .NET Core Web App, you could refer to this article.
It seems to be related to: Microsoft.Extensions.Configuration.AzureKeyVault
because as soon I add that nuget it stops working.
UPDATE: More info. it seems like the latest Microsoft.Extensions.Configuration.AzureKeyVault downgrade some of the configuration libs for me. And then it stops working.
I was having this same issue when using Host.CreateDefaultBuilder() and discovered that it was because my host environment was not Development.
To resolve this I added the following section to my launchSettings.json:
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
See related documentation:
The following defaults are applied to the returned HostBuilder:
load app IConfiguration from User Secrets when EnvironmentName is 'Development' using the entry assembly
In my case, I was missing the following NuGet package:
Microsoft.Extensions.Configuration.UserSecrets
Once I installed this, it worked perfectly.
Related
EDIT: This question has been significantly restructured, now I've figured out a bit more of the problem and this should clarify things.
I am following this tutorial: https://learn.microsoft.com/en-us/azure/app-service/tutorial-dotnetcore-sqldb-app
I've deployed my own multiproject app, which works, but I can't get the connection string working properly. For some reason, it only works if I hardcode the connection string into the OnConfiguring method of my DBContext class. Otherwise, it throws an error.
Like so:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
"Nasty hard coded azure connection string",
providerOptions => { providerOptions.EnableRetryOnFailure(); });
}
However, obviously, i want to get the connection string from a configuration file or environment variable.
Prior to deploying, I had the following. An extension method for IServiceColleciton which sets up the connection string:
public static void ConfigureSqlContext(this IServiceCollection services,
IConfiguration configuration) =>
services.AddDbContext<PeakedDbContext>(opts =>
opts.UseSqlServer(configuration.GetConnectionString("defaultConnection")));
then this method is called in program.cs. A pretty normal setup.
And I also set up an IDesignTimeDBContextFactory like so:
public class RepositoryContextFactory : IDesignTimeDbContextFactory<PeakedDbContext>
{
public PeakedDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<PeakedDbContext>()
.UseSqlServer(configuration.GetConnectionString("defaultConnection"));
return new PeakedDbContext(builder.Options);
}
}
Both my appsettings.json AND the Azure App Service configuration have the same name "defaultConnection".
As far as I can tell this is the approach recommended here: https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli
I have also tried adding an empty constructor for my DBContext. (Not sure how this affects things as I have other DIs on my DBContext constructor. My DBContext consructors are getting a bit out of hand:
public PeakedDbContext()
{
}
public PeakedDbContext(DbContextOptions options) : base(options)
{
}
public PeakedDbContext(DbContextOptions options, ICurrentUserService currentUserService) : base(options)
{
_currentUserService = currentUserService;
}
According the the second link, above, I shouldn't need OnConfiguring method in my DBContext... and even if I do, what is the correct way to pass access to configuration.GetConnectionString, instead of hardcoding the connection string? Should I just add yet another DBContext constructor with the config injected? However, it ONLY works if I have the onconfiguring method. Neither the contextfactory nor the extension method setup are being used by azure app service.
Shouldn't it use the designtime factory or the hostconfiguration extension method I've set up above? What is the right way to use _configuration.GetConnectionString("defaultConnection") so that it works both locally and on Azure Deployment?
Update:
Still no luck. I tried adding the database connection string as an environment variable on azure like so:
and then updating all my reference to getconnection string - in program.cs, IDesignFactory and OnConfiguring - like so:
Environment.GetEnvironmentVariable("PeakedDbConn")
This continues to work locally. But When deploying to Azure it claims the connection string in null... so it's not seeing this variable. Nor can I find any code that will access the defaultConnection from the image. Which is strange, because it accesses the SECRET variable just fine.
I have followed the same code which you have provided with few changes.
Check the below steps to get the Connection string from appsettings.json and override the value if Azure App Connection String has been set.
As you are using .NET Core 6, I have set all the Configurations in Program.cs itself.
My Program.cs
builder.Services.AddDbContext<MyDatabaseContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("MyDbConnection")));
builder.Configuration.AddEnvironmentVariables();
Same Connection String name must exist in both Local and Azure App Connection Strings.
My appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MyDbConnection": "Dummy Connection String"
}
}
Azure App Connection String:
To check if we are getting the Connection String Value, I have written code in Controller.
private readonly IConfiguration Configuration;
public HomeController(ILogger<HomeController> logger,IConfiguration config)
{
_logger = logger;
Configuration = config;
}
public IActionResult Index()
{
var myconnStr = Configuration.GetConnectionString("MyDbConnection");
ViewBag.myconnStr = myconnStr;
return View();
}
My .csproj file :
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="5.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
</ItemGroup>
</Project>
Local Output:
Deployed App Output:
Ok so it turned out to be GitHub actions that was the problem. The tutorial I followed in the first link doesn't mention this, perhaps because it's a single project api... not exactly sure.
The tutorial uses github actions to build and deploy the app, but for me, during the build it fails saying there is no connection string. This is because GitHub build process doesn't have access to either your local or the azure environment variable.
So I had to go into setting on my github repo and on the left click on secrets and variables < actions.
Click create a new Repository Secret, give it the same name as your environment variable i.e. PEAKEDDBCONN for me. Then, give it a value. I just used my local host string, but I guess you could type 'monkeynuts' in here if you wanted, it just needs to not be null.
Then you need to add a line to your workflow yaml file, the same one the tutorial talks about, to tell it about the environment variable. I added it like so:
jobs:
build:
runs-on: ubuntu-latest
env:
PEAKEDDBCONN: ${{ secrets.PEAKEDDBCONN }}
Then it all builds nicely and works.
For anybody who it helps, I decided to blog this whole setup:
https://garyfrewin.hashnode.dev/setting-up-an-entity-framework-core-web-api-on-azure-with-sqlserver-a-step-by-step-guide-for-hobby-projects
I've got an azure app service that I've set up like this:
But when I call IConfiguration.GetConnectionString("db") I get null back.
I've read articles like this https://mderriey.com/2018/08/21/azure-app-service-connection-strings-and-asp-net-core/ which say "it just works", but they're all several years old. I assume something's changed, but what?
Enumerating over all settings in my IConfiguration object I've got no connection strings. I do in development, where my appsettings.development.json has a connectionStrings: { db: "" } defined.
I can see and read the ENV variable: POSTGRESQLCONNSTR_db from within code, and it's value is correct (what I've set via the Azure portal).
Should I expect to be able to do IConfiguration.GetConnectionString("db")? Or am I expected to switch between reading env variables in prod vs dev.
Do I need to include some nuget package to make IConfiguration work under Azure with these ENV variables and their mad prefixes?
My startup.cs basically looks like:
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
}
public IConfiguration Configuration { get; }
Nothing else in there of interest to this question.
The POSTGRESQLCONNSTR_ prefix isn't supported by the environment variables configuration provider. The docs shows this, in an indirect fashion, where it states that the following prefixes are supported:
CUSTOMCONNSTR_
MYSQLCONNSTR_
SQLAZURECONNSTR_
SQLCONNSTR_
It's also apparent in the source code for the provider.
There are a couple of options for working around this:
Change the Type to Custom in the Connection strings section of the Azure portal.
Change to an Application setting of ConectionStrings:db in the Azure portal.
This is being tracked on GitHub: https://github.com/dotnet/runtime/issues/36123.
I got confused as well, so here it is:
You have two options to specify Connection String locally:
launchSettings.json (environmentVariables section)
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"SQLAZURECONNSTR_SomeConnectionString": "DefaultEndpointsProtocol=blah"
}
appSettings.json
"ConnectionStrings": {
"SomeConnectionString": "DefaultEndpointsProtocol=blah"
}
Having either way will allow you to get the connection string setting by calling:
IConfiguration.GetConnectionString("SomeConnectionString")
Function call above will also work when deployed to Azure, as it is using EnvironmentVariables configuration provider to read settings.
Instead of getting the config from the interface
IConfiguration.GetConnectionString("db")
try to get it from
Configuration.GetConnectionString("db")
And in production you have an empty string in production.appsetting.json and add the value in azure(appservice) configuration directly under connectionstrings(this will override the json setting file). And no nugets are needed for reading from appsettings
I have been learning the bot framework from microsoft and have been following a tutorial on pluralsight. I have made the changes to my Global.asax.cs and for some reason I keep on getting the error setting must be in the form name=value. I have no idea what to do and how to get rid of these errors. Any help would be highly appreciated.
Global.asax.cs
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// This code chunk below will allow us to use table bot data store instead of
// in memory data store
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
// here we grab the storage container string from our web config and connecting
var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);
builder.Register(c => store).
Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore).
AsSelf().
SingleInstance();
});
GlobalConfiguration.Configure(WebApiConfig.Register);
TableBotDataStore is for Azure Table Storage, and it seems you are using some sql connectionstring. You would usually have that connection string in the AppSettings section, not the ConnectionStrings section (yes, the names are a bit misguiding here).
I have an ASP.NET Core 2 application hosted on Azure, and I added a new Application Settings MyNewSetting for my App in the Azure Portal.
How do I access that setting from a controller?
My code bellow:
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<AppSecrets>(Configuration);
services.AddSingleton<ITableRepositories, TableClientOperationsService>();
//...
My Controller:
public class RecordController : Controller
{
const int MyNewSetting = 7; // this one to replace with Azure Setting one
private readonly ITableRepositories repository;
public RecordController(ITableRepositories rep) {
repository = rep;
}
Here, I need probably to add FromServices injection, but I am not sure if it will work...
EDIT:
Folowing the #dee_zg answer, the following code could probably do the job:
public class RecordController : Controller
{
int MyNewSetting = 7;
private readonly ITableRepositories repository;
public RecordController(ITableRepositories rep) {
repository = rep;
int myInt;
if (int.TryParse(System.Environment.GetEnvironmentVariable("MY_NEW_SETTING"),
out myInt)) {
MyNewSetting = myInt;
};
}
You can choose to either get them from AppSettings["your-key"] collection or as environment variables: Environment.GetEnvironmentVariable("your-key").
From there you can map them to your custom IOptions and inject wherever you need them.
There's quite a few things you can do.
Use Options and configuration objects
The options pattern uses custom options classes to represent a group of related settings. We recommended that you create decoupled classes for each feature within your app.
Use an IOptionsSnapshot.
IOptionsSnapshot supports reloading configuration data when the configuration file has changed. It has minimal overhead. Using IOptionsSnapshot with reloadOnChange: true, the options are bound to Configuration and reloaded when changed.
... (see documentation)
In short, have a look at Configuration in ASP.NET Core, determine the scenario that best fits your needs and have at it!
Hope this helps.
I have a Azure Mobile Services project. When running locally everything works fine, the Application_Start() method gets called which in turn calls my WebApiConfig.Register() method.
However, when published to a live Azure Mobile Services server the Application_Start() does not get called along with the WebApiConfig.Register().
In the servers log I have the following entry:
No bootstrapper found -- using default bootstrapper. A bootstrapper can be specified in one of two ways: Either by defining a public, static class with name 'WebApiConfig' having a public parameter-less member called 'Register', or using the 'IBootstrapper' attribute to define a public class with a default constructor.
Why is Azure Mobile Services not picking up my BootStrapping WebApiConfig?
public static class WebApiConfig
{
public static void Register()
{
Trace.TraceInformation("Hello from WebApiConfig Register().");
// Use this class to set configuration options for your mobile service
ConfigOptions options = new ConfigOptions();
// Use this class to set WebAPI configuration options
HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
// To display errors in the browser during development, uncomment the following
// line. Comment it out again when you deploy your service for production use.
// config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
}
}
public class WebApiApplication : System.Web.HttpApplication
{
public WebApiApplication()
{
Trace.TraceInformation("Hello from WebApiApplication ctor!");
}
protected void Application_Start()
{
Trace.TraceInformation("Hello from Application_Start()");
//RouteConfig.RegisterRoutes(RouteTable.Routes);
WebApiConfig.Register();
var dataContext = new DataContext();
dataContext.Database.Initialize(false);
}
}
Help is much appreciated!
That is bizarre... It really looks like you got it right. After working with .net backend azure mobile service for few weeks, I might suggest just maybe restart the service in portal and republish. I have hit some weird unexplained stuff just like you are and somehow fix like that.