MVCBeginnersSeptember 2026 · 6 min read

ASP.NET Core Configuration and EF Core: Connect the Right Database

By go2ismail · Published · .NET 10

TL;DR

Database setup connects three decisions: where settings come from, which EF Core provider is selected, and how the schema is created or evolved. A connection string alone does not complete all three.

The foundation: what the official documentation explains

ASP.NET Core configuration combines providers such as JSON files, environment variables, and development user secrets. Hierarchical settings can bind to C# objects. EF Core database registration then selects the provider and its options. EnsureCreated is a lightweight schema initialization API, while migrations track schema evolution; they should not be treated as interchangeable startup commands.

Implementation context: The examples use a .NET 10 MVC application organized into feature folders (Vertical Slice Architecture). Basic C# classes and async/await are assumed. Reference excerpts show selected parts of that application; separately labeled teaching adaptations explain alternatives. They are not complete standalone projects.

Follow the settings that the code actually reads

A common debugging mistake is adding ConnectionStrings:DefaultConnection because another tutorial used it, while the application reads a different section. The reference database registration binds DatabaseSettings into DatabaseSettingsModel. Its SQL Server connection string lives at DatabaseSettings:MsSQL:ConnectionString. Configuration names are part of the application contract.

The provider selection is an if/else chain: InMemory is checked first, SQL Server second, PostgreSQL third. Enable exactly one provider when using this design. If multiple switches are true, the first matching branch wins. If none is true, this method does not register AppDbContext at all. These are observations about this implementation, not universal ASP.NET Core precedence rules.

Reference excerpt: Infrastructures/Databases/DI.cs

var dbSettings = configuration.GetSection("DatabaseSettings").Get<DatabaseSettingsModel>();

        if (dbSettings?.InMemory?.IsUsed == true)
        {
            services.AddDbContext<AppDbContext>(options =>
                options.UseInMemoryDatabase(dbSettings.InMemory.ConnectionString)
            );
        }

Configure a local SQL Server example

The following is a teaching configuration fragment for a disposable local database. LocalDB requires a suitable Windows development installation. Merge these keys with the rest of the app configuration instead of replacing the complete settings file; other infrastructure still needs its own settings. Use the provider that is actually available on your machine.

Teaching configuration: local development only

{
  "DatabaseSettings": {
    "InMemory": { "IsUsed": false },
    "MsSQL": {
      "IsUsed": true,
      "ConnectionString": "Server=(localdb)\\MSSQLLocalDB;Database=MvcLearning;Trusted_Connection=True;",
      "TimeoutInSeconds": 30
    },
    "PostgreSQL": { "IsUsed": false }
  }
}

For a credential-bearing connection string, use development user secrets rather than committing the value. The corresponding key must retain the same hierarchy. In environment-variable configuration, the equivalent key is DatabaseSettings__MsSQL__ConnectionString. Changing the storage location does not change the key expected by the binder. User secrets are a development convenience, not an encrypted production vault.

Understand how the context uses configuration

The registration method calls UseSqlServer with the selected connection string and command timeout. The Country handler only asks for AppDbContext. It should not repeat the provider switch or parse JSON settings. That separation allows several features to share a consistent database setup.

The real AppDbContext also depends on a scope factory and current-user service for cross-cutting behavior. An abbreviated tutorial context with only DbContextOptions is therefore not a drop-in replacement for this class. When inspecting dependency-resolution failures, include all constructor dependencies, not only the provider package.

Notice the actual schema initialization strategy

The reference startup uses EnsureCreated() before seeding. It does not call Migrate() in that block. This matters when extending the entity model: starting the app again is not a general mechanism for altering an existing relational schema. A newly added property may exist in C# while the database still has its original table shape.

Reference excerpt: Program.cs

// Auto-create database and seed data
using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    dbContext.Database.EnsureCreated();

    // Seed roles, admin, currencies, tax, company
    await DatabaseSeeder.SeedAsync(scope.ServiceProvider);

    // Demo seeding (only runs if DemoMode:IsDemo = true)
    await DemoSeeder.SeedAsync(scope.ServiceProvider);
}

For a disposable experiment, an intentionally recreated learning database can help isolate the model. For a database that must retain data, plan schema evolution with migrations and a reviewed transition from any EnsureCreated-created schema. Do not casually combine both APIs or delete a database just to silence a missing-column exception.

Diagnose the failure at the correct layer

An unresolved AppDbContext suggests registration or dependencies. A connection failure suggests server availability, identity, network, or connection settings. A missing table or column suggests schema state. A successful connection followed by incorrect filtering suggests query behavior. Keeping these categories separate is faster than repeatedly changing the connection string.

The InMemory provider can simplify a local demonstration, but it does not establish relational behavior such as foreign-key enforcement or SQL translation. Validate provider-specific behavior against the intended relational provider. Reading the handler code alone cannot prove compatibility across every database.

A focused learning exercise

Write down the effective provider and settings path without displaying credentials. Trace that path through the binding model and registration method. Then inspect startup to identify who initializes the schema. You should be able to explain all three before adding a new entity or troubleshooting seeding. This exercise changes no source code and often reveals a configuration mismatch immediately.

Key Takeaways

  • Use the settings path read by your application, not one copied from another tutorial.
  • Select one provider and understand conditional registration.
  • EnsureCreated and migrations solve different schema-management needs.

FAQ

Why is my DefaultConnection ignored?

The reference reads DatabaseSettings:MsSQL:ConnectionString instead. Configuration keys must match the consuming code.

Will EnsureCreated add a new column to my existing table?

No. It is not a schema-migration mechanism for evolving existing tables.

Can I use InMemory to prove SQL Server behavior?

No. It is a different provider and does not reproduce all relational constraints or query behavior.