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?
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 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
When trying to scaffold a new controller with views in my project, I get the following error: (Yes, the image shows DEBUG connection... I changed them all to DEFAULT after I took the screenshot of the error)
(In case the image doesn't show)
There was an error running the selected code generator: 'A named connection string was used, but the name DefaultConnection 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.
However, I am using IConfiguration and both Startup.cs and database context file reference that connection.
When scaffolding the dbcontext, I can use DefaultConnection and it pulls in all the changes from the SQL database I'd expect to see in my project - just then refuses to scaffold a controller.
Startup.cs
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.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<CJFresh>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
Database Context File:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Name=DefaultConnection");
}
}
AppSettings.json
{
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Server=SERVER;Database=DATABASE;Trusted_Connection=False;user id=USERX;password=PASSWORDX;MultipleActiveResultSets=true",
}
}
I've tried some of the solutions I've seen in other posts, but they don't seem to work for me.
For example, I've tried all the edits in this post:
.Net Core 2 EF core connection string problem
I'm sure it's something simple I'm missing, but I just can't see it right now.
I solved this problem by copying the entire SQL Server connection string from appsettings.json and pasting it in place of the below code, within the UseSqlServer method. For an unknown reason the automatic Razor View build wizard in Visual Studio won't read from the json connection string variable. The error for me happened after I did a scaffold-dbcontext which created models and a db context class from database tables.
optionsBuilder.UseSqlServer("name=ACConnection");
We are working on a vary large ASP.NET Core MVC 1.0 application. We have 4-tiers to each of our applications as follows:
DTO
Repository (Entity Framework - Code First)
Service (Business Logic)
MVC (UI-MVC)
Currently, in our repositories, which handle all database operations we have hard coded the database connection strings in the DbContext as follows:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseSqlServer("Data Source=somedatabase.database.windows.net;Initial Catalog=database;Integrated Security=False;User ID=username;Password=password;Connect Timeout=60;Encrypt=True;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;MultipleActiveResultSets=true");
}
This project is outside the MVC project as a standalone ASP.NET Core 1.0 project. It also has a empty Program.cs file in it which seems to be required to execute the code-to-database command lines (dotnet ef migrations add and dotnet ef database update).
The reason we have a hard coded connection string in the DbConext is because when we use the following code, we get an object reference not set to an instance to an object exception, when executing the dotnet ef commands.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseSqlServer(ConfigurationManager.ConnectionStrings["StandardDatabase"].ConnectionString);
}
However, since we have a Program.cs, if we add a Debug.WriteLine for the connection string and run the project, it does return the correct connections string and if we set the connection string in the appsettings.json file in the UI, the UI will successfully connect as well.
THE ISSUE:
The above mentioned stack is what we use for several "Micro Apps", which means we have several projects that connect to several databases. We also want to take advantage of Development, Staging and Production connection strings.
If we use Configuration Manager Connection String, everything is good for daily operations; however, when ever we want to utilize Entity Frameworks code to database command lines, we need to go in to each repository we want to update and change the DbContext to a hard coded connection string, execute the commands, then change them back to when done, which becomes quite troublesome.
THE QUESTION:
Are we just doing this wrong, is there a preferred practice for setting up an Entity Framework Core 1.0 stack which allows us not to manually have to change the DbContext but take advantage of configuration files across the board?
Any direction would be appreciated!
EF Core is intended to be configured via dependency injection. Dependency injection keeps your DbContext clean, and independent of implementation details of the environment.
Your initial solution of hard-coding connection strings tightly coupled the DbContext to the knowledge of where the database is located. That's obviously a problem. But your proposed solution tightly couples the DbContext to the knowledge of a particular configuration file. That, too, is a problem.
To keep the DbContext independent of environmental details, create a constructor that takes a DbContextOptions parameter and calls the base class constructor.
public class MyContext : DbContext
{
public MyContext(DbContextOptions options) :
base(options)
{
}
}
Do this instead of overriding OnConfiguring. Then initialize it in the Startup.cs of your host application. That's where the knowledge of the configuration file belongs.
public class Startup
{
private IConfigurationRoot _configuration;
public Startup(IHostingEnvironment env)
{
_configuration = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json")
.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfigurationRoot>(_configuration);
services.AddDbContext<MyContext>(options => options
.UseSqlServer(_configuration.GetConnectionString("MyContext")));
}
}
Now you can use your DbContext from anywhere.
ANSWER: I was making this much more difficult then it actually was. I followed Juunas' advise and added in the following code in my Repository DbContext Class:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// get the configuration from the app settings
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
// define the database to use
optionsBuilder.UseSqlServer(config.GetConnectionString("StandardDatabase"));
}
Which works perfect with the dotnet ef command line tools and far as the multiple environment setup goes with my MVC UI sticking with the following default code in my startup.cs works great as well.
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("project.json", optional: true, reloadOnChange: true);
IDbContextFactory might also help. EF Command Line Tools and DI can use this factory to create instances of your DBContext. Design Time services (e.g. Migrations) will discover implementations of this interface that are in the same assembly as the derived context.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace MyProject
{
public class BloggingContextFactory : IDbContextFactory<BloggingContext>
{
public BloggingContext Create()
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite("Filename=./blog.db");
return new BloggingContext(optionsBuilder.Options);
}
}
}
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.