Kyle Rego

Notes on new ASP.NET Core API setup with Swagger, EF Core, SQLite

Today I made a new ASP.NET Core project for a hobby website; these are some notes on what I did which is mainly similar to what I’ve settled on in my past projects.

Use dotnet to make a new web API in the output directory that you want (it will make the directory too):

dotnet new webapi -o GobGuides cd GobGuides

Create a .gitignore file with at least bin/ and obj/.

Now add these packages for Entity Framework Core:

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.AspNetCore.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Design

Create a derived DbContext to serve as your application’s database context class, I put mine in a folder/namespace Data:

using Microsoft.EntityFrameworkCore;

namespace GobGuides.Data;

public class AppDbContext(DbContextOptions opts) : DbContext(opts)
{

}

In the appsettings.Development.json, add some SQLite database connection string to the JSON:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "SQLiteDbConnString": "Data Source=gob_guides_dev.db"
}

Now edit Program.cs to like this:

using Microsoft.EntityFrameworkCore;

using GobGuides.Data;

var builder = WebApplication.CreateBuilder(args);

string sqliteConn = builder.Configuration["SQLiteDbConnString"]
    ?? throw new InvalidOperationException(
        "Database connection string missing");

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>(options =>
                                    options.UseSqlite(sqliteConn));

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    dbContext.Database.Migrate();
}

app.UseHttpsRedirection();

if (app.Environment.IsDevelopment())
{
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
        options.RoutePrefix = string.Empty;
    });
}

app.MapControllers();

app.Run();

I believe InvalidOperationException is a standard practice exception for when configuration data is missing.

This part:

using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    dbContext.Database.Migrate();
}

will make it so starting the app executes any outstanding migrations, that would be found in the same assembly and scaffolded with dotnet-ef. If the migrations are in a different assembly, the way the app database context is registered in the services will have to be like this:

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    {
    options.UseSqlite
        (
            builder.Configuration.GetConnectionString("Database"),
            b => b.MigrationsAssembly("Infrastructure")
        );
    }
);

The web API project file would also need a project reference to the Infrastructure project.

This is a nice way to setup the Swagger UI to be served at / instead of /swagger, which is convenient for development:

if (app.Environment.IsDevelopment())
{
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
        options.RoutePrefix = string.Empty;
    });
}

When the app is started, it will create the SQLite database, gob_guides_dev.db in this example. Files gob_guides_dev.db-shm, gob_guides_dev.db-wal will also be created when the database is used; in my case I add all three to the .gitignore.

This project is maintained by KyleRego