Run ef-core in a single file!
Theodor Heiselberg

Theodor Heiselberg @sukkergris

About: Developer since 2008

Location:
Denmark
Joined:
Jan 11, 2024

Run ef-core in a single file!

Publish Date: Jun 26
0 0

I just like this so much!

Experimenting with ef-core just got so much simpler.

With a simple script like this you get instant access to ef-core's generated sql!

// Kristoffer.cs
#:package Microsoft.EntityFrameworkCore.Design@10.0.0-preview.5.25277.114
#:package Npgsql.EntityFrameworkCore.PostgreSQL@10.0.0-preview.5
#:package Microsoft.EntityFrameworkCore@10.0.0-preview.5.25277.114
#:package Testcontainers@4.6.0
#:package Testcontainers.PostgreSql@4.6.0

// .NET Usings
using Microsoft.EntityFrameworkCore;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using Microsoft.Extensions.Logging;

// --- Application Logic ---

Console.WriteLine("🚀 Starting PostgreSQL container...");

await using var postgresContainer = new ContainerBuilder()
    .WithImage("postgres:16-alpine")
    .WithPortBinding(5432, true) // Map to a random host port
    .WithEnvironment("POSTGRES_DB", "mydatabase")
    .WithEnvironment("POSTGRES_USER", "myuser")
    .WithEnvironment("POSTGRES_PASSWORD", "mypassword")
    .WithWaitStrategy(Wait.ForUnixContainer().UntilCommandIsCompleted("pg_isready -U myuser -d mydatabase"))
    .Build();

await postgresContainer.StartAsync();

var connectionString = $"Host={postgresContainer.Hostname};Port={postgresContainer.GetMappedPublicPort(5432)};Database=mydatabase;Username=myuser;Password=mypassword;Include Error Detail=true;";

Console.WriteLine($"✅ PostgreSQL container running on: {postgresContainer.Hostname}:{postgresContainer.GetMappedPublicPort(5432)}");

// Configure and use the database context
var options = new DbContextOptionsBuilder<AppDbContext>()
    .UseNpgsql(connectionString)
    .EnableDetailedErrors().
            EnableSensitiveDataLogging().
            LogTo(Console.WriteLine,
                LogLevel.Information)
    .Options;

await using var dbContext = new AppDbContext(options);

await dbContext.Database.EnsureCreatedAsync();

// --- Data Operations ---

Console.WriteLine("\nAdding a new product...");
var newProduct = new Product { Name = "Keyboard", Price = 75.00m };
dbContext.Products.Add(newProduct);
await dbContext.SaveChangesAsync();
Console.WriteLine($"Added product: '{newProduct.Name}'");

Console.WriteLine("\nFetching all products...");
var products = await dbContext.Products.TagWith("Hello").TagWith("World").ToListAsync();
foreach (var product in products)
{
    Console.WriteLine($"  - ID: {product.Id}, Name: {product.Name}, Price: {product.Price:C}");
}

Console.WriteLine("\nTestcontainers operation completed. Stopping container...");


// --- Entity and DbContext Definitions ---

public class Product
{
    public int Id { get; set; }
    public required string Name { get; set; }
    public decimal Price { get; set; }
}

public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Seed initial data
        modelBuilder.Entity<Product>().HasData(
            new Product { Id = 1, Name = "Laptop", Price = 1200.00m },
            new Product { Id = 2, Name = "Mouse", Price = 25.00m }
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Requirements

  1. dotnet 10 sdk/runtime
  2. docker or podman

Result:

> dotnet run Kristoffer.cs
__________________________________________________
Project "/Users/sukkerfrit/private/headles-cms/Kristoffer.csproj" (Build target(s)):

/Users/sukkerfrit/private/headles-cms/Kristoffer.cs(75,27): warning CS8618: Non-nullable property 'Products' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
Done building project "Kristoffer.csproj".
🚀 Starting PostgreSQL container...
[testcontainers.org 00:00:00.05] Connected to Docker:
  Host: unix:///var/run/docker.sock
  Server Version: 28.2.2
  Kernel Version: 6.10.14-linuxkit
  API Version: 1.50
  Operating System: Docker Desktop
  Total Memory: 26.14 GB
  Labels: 
    com.docker.desktop.address=unix:///Users/sukkerfrit/Library/Containers/com.docker.docker/Data/docker-cli.sock
[testcontainers.org 00:00:00.14] Docker container 40986fe4400b created
[testcontainers.org 00:00:00.16] Start Docker container 40986fe4400b
[testcontainers.org 00:00:00.23] Wait for Docker container 40986fe4400b to complete readiness checks
[testcontainers.org 00:00:00.23] Docker container 40986fe4400b ready
[testcontainers.org 00:00:00.29] Docker container 8258a64bb442 created
[testcontainers.org 00:00:00.30] Start Docker container 8258a64bb442
[testcontainers.org 00:00:00.37] Wait for Docker container 8258a64bb442 to complete readiness checks
[testcontainers.org 00:00:00.38] Execute "/bin/sh -c pg_isready -U myuser -d mydatabase" at Docker container 8258a64bb442
[testcontainers.org 00:00:01.43] Execute "/bin/sh -c pg_isready -U myuser -d mydatabase" at Docker container 8258a64bb442
[testcontainers.org 00:00:01.49] Docker container 8258a64bb442 ready
✅ PostgreSQL container running on: 127.0.0.1:60363
warn: 6/27/2025 08:03:20.934 CoreEventId.SensitiveDataLoggingEnabledWarning[10400] (Microsoft.EntityFrameworkCore.Infrastructure) 
      Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data; this mode should only be enabled during development.
info: 6/27/2025 08:03:21.064 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executed DbCommand (20ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END
      FROM pg_class AS cls
      JOIN pg_namespace AS ns ON ns.oid = cls.relnamespace
      WHERE
              cls.relkind IN ('r', 'v', 'm', 'f', 'p') AND
              ns.nspname NOT IN ('pg_catalog', 'information_schema') AND
              -- Exclude tables which are members of PG extensions
              NOT EXISTS (
                  SELECT 1 FROM pg_depend WHERE
                      classid=(
                          SELECT cls.oid
                          FROM pg_class AS cls
                                   JOIN pg_namespace AS ns ON ns.oid = cls.relnamespace
                          WHERE relname='pg_class' AND ns.nspname='pg_catalog'
                      ) AND
                      objid=cls.oid AND
                      deptype IN ('e', 'x')
              )
info: 6/27/2025 08:03:21.120 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executed DbCommand (12ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Products" (
          "Id" integer GENERATED BY DEFAULT AS IDENTITY,
          "Name" text NOT NULL,
          "Price" numeric NOT NULL,
          CONSTRAINT "PK_Products" PRIMARY KEY ("Id")
      );
info: 6/27/2025 08:03:21.121 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      INSERT INTO "Products" ("Id", "Name", "Price")
      VALUES (1, 'Laptop', 1200.0);
      INSERT INTO "Products" ("Id", "Name", "Price")
      VALUES (2, 'Mouse', 25.0);
info: 6/27/2025 08:03:21.123 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT setval(
          pg_get_serial_sequence('"Products"', 'Id'),
          GREATEST(
              (SELECT MAX("Id") FROM "Products") + 1,
              nextval(pg_get_serial_sequence('"Products"', 'Id'))),
          false);

Adding a new product...
info: 6/27/2025 08:03:21.167 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executed DbCommand (7ms) [Parameters=[@p0='Keyboard' (Nullable = false), @p1='75.00'], CommandType='Text', CommandTimeout='30']
      INSERT INTO "Products" ("Name", "Price")
      VALUES (@p0, @p1)
      RETURNING "Id";
Added product: 'Keyboard'

Fetching all products...
info: 6/27/2025 08:03:21.240 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) 
      Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      -- Hello
      -- World

      SELECT p."Id", p."Name", p."Price"
      FROM "Products" AS p
  - ID: 1, Name: Laptop, Price: $1,200.00
  - ID: 2, Name: Mouse, Price: $25.00
  - ID: 3, Name: Keyboard, Price: $75.00

Testcontainers operation completed. Stopping container...
[testcontainers.org 00:00:02.02] Delete Docker container 8258a64bb442
Enter fullscreen mode Exit fullscreen mode

Enjoy :)

Comments 0 total

    Add comment