Introduction
Learn how to use DbContextOptionsBuilder, which provides a simple API for configuring DbContextOptions.
Using DbContextOptionsBuilder has two purposes: the first is to clean up code in Program.cs, and the second is to provide individual paths for development, production, and staging.
ChatGPT
ChatGPT was used to show how to implement some of the code presented here.
Conventional DbContext configuration
A developer will set up the configuration for an ASP.NET Core project by setting up the connection string in appsettings.json, as shown below.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=NorthWind2024;Integrated Security=True;Encrypt=False"
}
}
In Program.cs uses the following code for development to configure a DbContext.
builder.Services.AddDbContext<Context>(options =>
options.UseSqlServer(connectionString)
.LogTo(message => Debug.WriteLine(message), LogLevel.Information)
.EnableSensitiveDataLogging());
While an experienced developer will use code as shown for separate environments.
if (builder.Environment.IsDevelopment())
{
builder.Services.AddDbContext<Context>(options =>
options.UseSqlServer(connectionString)
.LogTo(message => Debug.WriteLine(message), LogLevel.Information)
.EnableSensitiveDataLogging());
}
else
{
builder.Services.AddDbContext<Context>(options =>
options.UseSqlServer(connectionString)
.LogTo(new DbContextToFileLogger().Log,
[DbLoggerCategory.Database.Command.Name],
LogLevel.Information));
}
Using DbContextOptionsBuilder first iteration
Create a class using DbContextOptionsBuilder to configure a DbContext.
internal class ContextOptions
{
public static DbContextOptionsBuilder<Context> Development(string connectionString)
{
var options = new DbContextOptionsBuilder<Context>()
.UseSqlServer(connectionString)
.LogTo(message => Debug.WriteLine(message), LogLevel.Information)
.EnableSensitiveDataLogging();
return options;
}
public static DbContextOptionsBuilder<Context> Production(string connectionString)
{
var options = new DbContextOptionsBuilder<Context>()
.UseSqlServer(connectionString)
.LogTo(new DbContextToFileLogger().Log, [DbLoggerCategory.Database.Command.Name],
LogLevel.Information);
return options;
}
}
Which is used in Program.cs as shown below
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
if (builder.Environment.IsDevelopment())
{
builder.Services.AddDbContext<Context>(options =>
ContextOptions.Development(connectionString));
}
else
{
builder.Services.AddDbContext<Context>(options =>
ContextOptions.Production(connectionString));
}
Using DbContextOptionsBuilder using generics
In the code above
The class ContextOptions is tied to a specific DbContext which is fine when there is one DbContext but does not lend itself to multiple DbContext in the same project
The class is tied to one project.
A better path is to create a class project with a class to configure the DbContext using generics, so any project can use the code.
Create a new class project which allows one or more projects to use the following class in the new class project.
T represents a DbContext.
public class ContextOptions
{
public static DbContextOptionsBuilder<T> Development<T>(string connectionString) where T : DbContext
{
var options = new DbContextOptionsBuilder<T>()
.UseSqlServer(connectionString)
.LogTo(message => Debug.WriteLine(message), LogLevel.Information)
.EnableSensitiveDataLogging();
return options;
}
public static DbContextOptionsBuilder<T> Production<T>(string connectionString) where T : DbContext
{
var options = new DbContextOptionsBuilder<T>()
.UseSqlServer(connectionString)
.LogTo(new DbContextToFileLogger().Log, [DbLoggerCategory.Database.Command.Name],
LogLevel.Information);
return options;
}
}
Add the class project to an ASP.NET Core project as a dependency.
In Progam.cs configure a DbContext.
var connectionString = builder.Configuration.GetConnectionString(nameof(ConnectionStrings.DefaultConnection));
if (string.IsNullOrEmpty(connectionString))
{
throw new InvalidOperationException($"The connection string {nameof(ConnectionStrings.DefaultConnection)} is not configured.");
}
builder.Services.AddDbContextPool<Context>(_ => { });
var options = builder.Environment.IsDevelopment() ?
ContextOptions.Development<Context>(connectionString) :
ContextOptions.Production<Context>(connectionString);
builder.Services.AddSingleton(options.Options);
Configure against either developer or production environment using a ternary operator.
Add DbContext options.
Improvements
If other configuration options are needed, additional methods can be added to the ContextOptions class, or another class can be added to handle other configurations.
If working with multiple data providers, create addition class projects and consider the following.
Consider creating a local NuGet package. By creating a local NuGet package, a developer can easily add the functionality to any project.
Source code
Class project ASP.NET Core project
Logging to file
NuGet package EntityCoreFileLogger provides logging for EF Core.
Requires the following in the project file.
<Target Name="MakeLogDir" AfterTargets="Build">
<MakeDir Directories="$(OutDir)LogFiles\$([System.DateTime]::Now.ToString(yyyy-MM-dd))" Condition="'$(Configuration)' == 'Debug'" />
</Target>
Note The condition is for Debug, so when the first build is done, the folder is created but not used for debug; the logging is for the production environment.
Preparing to run the source code.
- Create NorthWind2024 under LocalDb.
- Populate NorthWind2024 using the following script.
Summary
Using the code provided, either version allows a developer to easily configure a DbContext consistently in all their projects.
AI usage
A great use for using AI is when a developer finds code such as for the generic code presented which a developer finds but does not know how to use which can happens when code is found in a forum post. For this, see ChatInstructions.md in the class project which shows how in this case ChatGPT can be helpful.