I have an ASP.NET 5 web application that has pulled in a .NET 4.6 class library. At some point in the class library there is a call to get a connection string from web.config:
ConfigurationManager.ConnectionStrings["AppDataConnectionString"].ConnectionString
(NOTE: I CAN NOT CHANGE THIS CODE.)
This class library has been used in old web forms applications, where the AppDataConnectionString was defined in their web.configs. Now I'm trying to use the class library in my ASP.NET 5 web app, but the above code throws a null reference exception.
Here is the connection strings section in my web.config in the ASP.NET 5 project:
<connectionStrings>
<add name="AppDataConnectionString" connectionString="server=xxxxxx;database=yyyyy;Trusted_Connection=yes;" providerName="System.Data.SqlClient" />
</connectionStrings>
I've also tried adding it in an appsettings.json file as follows:
{
"ConnectionStrings": {
"AppDataConnectionString": {
"ConnectionString": "server=xxxxx;database=yyyyy;Trusted_Connection=yes;"
}
}
}
Here is where I load the configuration in Startup:
public IConfiguration _Configuration { get; set; }
public Startup()
{
_Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(provider => _Configuration);
}
public void Configure(IApplicationBuilder app)
{
// Call some code from the class library that tries to get that connection string.
}
It seems to me like the configuration manager just can't find where the connection string is. Do I have my web.config or appsettings.json structured in the right way for it to find it?
It turns out that any configuration that was stored previously in a web.config file in previous versions of ASP.net now needs to be stored in an app.config file in the same directory as project.json in ASP.net 5.0. Now my imported class libraries can get their connections strings and everything works.
Related
I am trying to run 'update-database -verbose' in the Package Manager Console but I am getting the following lines at the end of the output: (nothing is being generated in my SQL server)
Using context 'TutoringContext'.
Finding design-time services for provider 'Microsoft.EntityFrameworkCore.SqlServer'...
Using design-time services from provider 'Microsoft.EntityFrameworkCore.SqlServer'.
Finding design-time services referenced by assembly 'LakeTutoringWebsite'...
Finding design-time services referenced by assembly 'LakeTutoringWebsite'...
No referenced design-time services were found.
Finding IDesignTimeServices implementations in assembly 'LakeTutoringWebsite'...
No design-time services were found.
I see that I can create a DesignTimeDbContextFactory like this: https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli
But the constructor must be parameterless so I can't pass an IConfiguration object to get my connection string. How can I run 'update-database' without hard coding my connection string?
Based on the above link, I thought I would be able to run 'update-database' since I have added my DBContext to services.
I am using dependency injection for my DBContext in my project:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<TutoringContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LakeTutoringDatabase")));
}
}
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class TutoringContext : IdentityDbContext<IdentityUser>
{
public DbSet<Comment> Comments { get; set; }
public TutoringContext(DbContextOptions<TutoringContext> options) : base(options)
{
}
}
I am using ASP.NET Core 3.1
I installed System.Configuration.Configuration version 6.0.0 using NuGet.
Firstly, as well as creating an implementation of IDesignTimeDbContextFactory, your project will need to add a reference to package:
Microsoft.EntityFrameworkCore.Design
Normally, hard-coded strings are not so bad in this class because you are just working with a development database. Ideally, you would apply migrations to other databases (staging, beta, production) in code (instead of update-database in command line) at application startup which will use values from your appsettings.json file accessed thru IConfiguration.
However, if you do want to have the development connection string accessed from a json file, this link does a pretty good job of walking you thru it.
I added a Startup.cs to my .NET 6 console application as described here.
To avoid having to manually specify the connection string when calling Scaffold-DbContext, I tried to provide the connection string from the application configuration like this:
Scaffold-DbContext -Connection name=DatabaseConnectionName -Provider...
For some reason, this does not seem to work and I get the following error:
System.InvalidOperationException: A named connection string was used, but the name 'DatabaseConnectionName' was not found in the application's configuration. Note that named connection strings are only supported when using 'IConfiguration' and a service provider, such as in a typical ASP.NET Core application. See https://go.microsoft.com/fwlink/?linkid=850912 for more information.
Here is the content of my Startup.cs:
public class Startup
{
IConfiguration Configuration { get; }
public Startup()
{
IConfigurationBuilder builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkNpgsql().AddDbContext<DatabaseContext>(opt =>
opt.UseNpgsql(Configuration.GetConnectionString("DatabaseConnectionName")));
services.AddSingleton(Configuration);
}
}
And this is the content of my appsettings.json:
{
"ConnectionStrings": {
"DatabaseConnectionName": "User ID=...;Password=...;Server=...;Port=5432;Database=database-name;Integrated Security=true;Pooling=true"
}
}
I noticed that this error only occurs while trying to execute the Scaffold-DbContext command, but not when I am running the application normally. I think that this problem might be related to the fact that I am manually creating an instance of the Startup class in my Program.cs, but I don't know if that is true or what I can do about this.
Is there any way how I can fix this error and let the command read the connection string from the configuration file?
I have a library in a large asp.net 4.8 site that uses the ConfigurationManager deep down in the bowels of the project. At the time, no one had a problem with the dependency on that library. Well, we're upgrading to asp.net 5 (i.e. Core) which doesn't really use the ConfigurationManager anymore, instead uses a DI version of IConfguration. Unfortunately the access of the connection strings is so deep, passing along this object from the Controller down through the libraries isn't feasible. Is there any kind of "ConfigurationManager.ConnectionStrings" equivalent that reads the application.json file's ConnectionStrings section?
You can use the following Code
public class SampleClass
{
private readonly IConfiguration _config;
public SampleClass(IConfiguration config)
{
_config = config;
}
public string GetConnectingString(string name)
{
return _config.GetConnectionString(name);
}
}
Assuming you have a connectionstrings section in your appsettings.json, you can use the GetConnectionString() method on the IConfiguration.
connectionString = configuration.GetConnectionString("DefaultConnection");
https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.configurationextensions.getconnectionstring?view=dotnet-plat-ext-6.0
I have added key value pair in Azure app Configuration and trying to read it in Startup.cs class file.
Please suggest how to do that.
public class Startup : FunctionsStartup
{
private static readonly string url= "*******************";
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
string connectionString=????? //How to get this value from Azure app config
builder.Services.AddDbContext<DbContext, Context>(
options => SqlServerDbContextOptionsExtensions.UseSqlServer(options, connectionString));
builder.ConfigurationBuilder.AddAzureAppConfiguration(url);
}
}
You need to split out configuration of your configuration from the registration of your application services. In other words, setup of Azure App Configuration should be done in ConfigureAppConfiguration while the registration of your DbContext should be done from the Configure method. This will allow you to access the Configuration property of the FunctionsHostBuilderContext in the Configure method to retrieve your connection string:
public class Startup : FunctionsStartup
{
private static readonly string url= "*******************";
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
// register AzureAppConfiguration only
builder.ConfigurationBuilder.AddAzureAppConfiguration(url);
}
public override void Configure(IFunctionsHostBuilder builder)
{
var context = build.GetContext();
var config = context.Configuration;
string connectionString = config["AzureAppConfigKeyName"];
builder.Services.AddDbContext<DbContext, Context>(
options => SqlServerDbContextOptionsExtensions.UseSqlServer(options, connectionString)
);
}
}
The value you pass to the indexer of the config object is going to depend on what you named your key in Azure App Configuration. Assuming you called it "DatabaseConnectionString" then that is exactly what you'll pass, ie: config["DatabaseConnectionString"].
Prefixes
Azure App Configuration has a few features that can control how the key names are handled by the application, specifically the ability to "Trim Prefixes". You aren't using that here (since you are just passing the URL) but suppose you had keys in Azure App Configuration such as MyFunction:DatabaseConnectionString. By default you would access this using it's full name:
var cs1 = config["MyFunction:DatabaseConnectionString"];
// or
var cs2 = config.GetSection("MyFunction")["DatabaseConnectionString"];
You could however specify the long-form connection options. This would allow you to only select keys that begin with that prefix and optionally trim them off:
builder.ConfigurationBuilder.AddAzureAppConfiguration(s =>
{
s.Connect(url, new DefaultAzureCredential());
s.Select("MyFunction:*");
s.TrimKeyPrefix("MyFunction:");
});
Which would now make your connection string available with a shorter key:
string connectionString = config["DatabaseConnectionString"];
This feature is especially useful if you have a lot of different settings in your Azure App Configuration instance and only want to pull in those specifically related to your application (labels can also be used for this purpose).
Azure App Config Connection - Environment Variables
Finally, a suggestion. Don't store the url or connection details of your Azure App Configuration instance hard-coded in your application. Make use of environment variables for this. "Application Settings" in Azure App Service/Azure Functions are automatically added as environment variables to your application.
Before ConfigureAppConfiguration has run, a default set of configuration sources has already been added to the context/builder. These include host.json, appsettings.json, local.settings.json (when running locally) and environment variables. A "better" way to configure your link to Azure App Configuration is to use this mechanism.
For example, you can add a FunctionApp Application Setting named AzureAppConfigUrl that contains this value:
Locally you'd add a corresponding entry to your local.settings.json file:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"AzureAppConfigUrl": "https://your-configuration.azconfig.io"
}
}
You'd then reference this value when configuring Azure App Configuration. Locally it will pull from the JSON file and when running in Azure it will read from the Application Settings (or rather, the environment variables):
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
var context = build.GetContext();
var url = context.Configuration["AzureAppConfigUrl"];
builder.ConfigurationBuilder.AddAzureAppConfiguration(url);
}
ConfigureAppConfiguration method should be used exclusively to configure application's IConfiguration object. There is another dedicated method Configure which should be used to configure application itself (e.g. dependency injection).
Here is the simple example:
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
builder.ConfigurationBuilder
.SetBasePath(context.ApplicationRootPath)
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
}
public override void Configure(IFunctionsHostBuilder builder)
{
IConfiguration configuration = builder.Services.BuildServiceProvider().GetRequiredService<IConfiguration>();
// use configuration object
}
If that doesn't work for you for some reason, you can always fallback to get the value from environmental variables, since settings are added to Environment
Environment.GetEnvironmentVariable("DatabaseConnectionString"))
The doc below walks you through how to use Azure App Configuration in Azure Functions
https://learn.microsoft.com/azure/azure-app-configuration/quickstart-azure-functions-csharp
Linqpad 6 supports .Net Core.
When I create a new empty .Net Core API solution in visual studio , I get a simple template with a simple demo controller.
When I run it in visual studio it uses a command-line server (kestrel) to run the project :
So I wanted to see if I can run this project in Linqpad 6.
So I've installed all nugets and copied the code to Linqpad :
https://i.stack.imgur.com/lwRyU.png
void Main()
{
CreateWebHostBuilder(new string[] { "" }).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
}
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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
I do see that it is now listening :
But calls to http://localhost:5000/api/values do get acked , but without the json value response from controller :
Question:
How can I get Linqpad to return the value from the controller? ( a simple json)
There's a difference in the way LINQPad executes code that could account for this not working.
Being a scripting tool, LINQPad wraps everything in a class (otherwise, the Main method would have nowhere to live). So ValuesController is actually ends up as a nested type, UserQuery.ValuesController, and this could potentially upset the routing API.
For such situations, LINQPad has the ability to extract all nested types and move them outside UserQuery (using the Roslyn API). To enable this, add the following to the start of your query:
#LINQPad nonest
Something else to consider is that a default MVC project includes an appsettings.json file. Should this be required for your code in LINQPad, you need to create such a file and add a reference to it (when you reference to a non-binary file, LINQPad copies it into the output folder, which is exactly where appsettings.json needs to be).
Edit: There's now a checkbox in the Query Properties dialog to add ASP.NET Core references to a query in LINQPad 6. This pulls the assemblies straight from the shared framework folder, and is easier than finding the right NuGet packages.