EF Core DbContextOptionsBuilder
Karen Payne

Karen Payne @karenpayneoregon

About: Microsoft MVP, Microsoft TechNet author, Code magazine author, developer advocate. Have a passion for driving race cars.

Location:
Oregon, USA
Joined:
Jan 1, 2023

EF Core DbContextOptionsBuilder

Publish Date: Jun 15
1 0

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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());
Enter fullscreen mode Exit fullscreen mode

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));

}
Enter fullscreen mode Exit fullscreen mode

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;

    }
}
Enter fullscreen mode Exit fullscreen mode

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));
}
Enter fullscreen mode Exit fullscreen mode

Using DbContextOptionsBuilder using generics

In the code above

  1. 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

  2. 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;

    }
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode
  1. Configure against either developer or production environment using a ternary operator.

  2. Add DbContext options.

shows above with pointer

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>
Enter fullscreen mode Exit fullscreen mode

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.

See also

Comments 0 total

    Add comment