VaRe&DaIn–part 060 of n–OptionsBuilder

Now I want to create the database in each provider (Sqlite, MsSql, Postgresql, Mongo)

Let’s say that I have the database context

public partial class EmpContext : DbContext
{
    public EmpContext()
    {
        
    }

    public EmpContext(DbContextOptions<EmpContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Department> Department { get; set; }
    public virtual DbSet<Employee> Employee { get; set; }
}

and I want to create the database from a simple connection string
For this I need a common interface , IEFCore_DatabasePlugin, to function the same for all database

public interface IEFCore_DatabasePlugin : IDatabasePlugin
{
    DbContextOptionsBuilder<T> GenerateDbContextOptionsBuilder<T>(string connectionString, string? databaseName = null)
        where T : DbContext;

    DbContextOptionsBuilder<T>  UsePlugin<T>(DbContextOptionsBuilder<T> optionBuilder,string connectionString, string? databaseName = null)
        where T : DbContext;    

}

I target .NET 10 and Entity Framework Core 10.0.11. I support SQL Server,
PostgreSQL, SQLite, and MongoDB while keeping my application-facing code the
same.

How the solution is used

I demonstrate the intended usage in my tests: I choose a plugin, build options
for my application context, create the database when necessary, and then use
the context normally. I keep the provider-specific part in the setup code.

SQLite example

My SQLite test creates a unique temporary file and builds a connection string
for it:

var databasePath = Path.Combine(
    Path.GetTempPath(),
    $"efcore_dain_sqlite_{Guid.NewGuid():N}.db");

var connectionString =
    new SqliteConnectionStringBuilder
    {
        DataSource = databasePath
    }.ConnectionString;

I select the SQLite plugin and ask it to configure `EmpContext`:

IEFCore_DatabasePlugin plugin = new SqliteEfCorePlugin();
var optionsBuilder =
    plugin.GenerateDbContextOptionsBuilder<EmpContext>(
        connectionString,
        databasePath);

using var context = new EmpContext(optionsBuilder.Options);
context.Database.EnsureCreated();

Assert.True(File.Exists(databasePath));

`EnsureCreated` exercises my complete path: the plugin selects SQLite, EF Core
receives the configured options, and EF Core creates the database from my
`EmpContext` model. I then verify the observable result by checking that the
file exists. My context contains no SQLite-specific configuration.

SQL Server, PostgreSQL, and MongoDB example

My container tests use the same context setup, but obtain the connection string
from a Testcontainers-managed database:

var connectionString = dockerContainer.GetConnectionString();
var optionsBuilder =
    plugin.GenerateDbContextOptionsBuilder<EmpContext>(
        connectionString,
        "EmpContextDatabase");

using var context = new EmpContext(optionsBuilder.Options);
context.Database.EnsureCreated();

Takeaways

My main design choice is to isolate provider configuration, not to hide every
database difference. I keep connection-string rules and EF Core extension
methods in the provider packages, while my application depends on a small,
stable plugin contract.


Posted

in

,

by

Tags: