1) Installer pakker
Fra en tom Web API:
bash · 9 lines
1dotnet new webapi -n Demo.Api2 3// EF Core runtime4dotnet add Demo.Api package Microsoft.EntityFrameworkCore5dotnet add Demo.Api package Microsoft.EntityFrameworkCore.SqlServer6 7// EF Core tooling til CLI8dotnet tool install --global dotnet-ef9dotnet add Demo.Api package Microsoft.EntityFrameworkCore.DesignHvorfor disse pakker?
Microsoft.EntityFrameworkCore– selve EF Core runtime.Microsoft.EntityFrameworkCore.SqlServer– provider til SQL Server.Microsoft.EntityFrameworkCore.Design– krævet for migrations fra CLI.dotnet-ef– giver digdotnet efkommandoerne i terminalen.
Fejlsøgning
- “No executable found matching command ‘dotnet-ef’”: Sørg for at tool’et er installeret globalt, og at din PATH er opdateret.
- Hvis du har flere projekter, så brug
--projectfor at pege på dit API-projekt.
2) Opret din model
csharp · 9 lines
1namespace Demo.Api.Models;2 3public class Product4{5 public int Id { get; set; } // PK6 public string Name { get; set; } = "";7 public decimal Price { get; set; }8 public bool IsActive { get; set; } = true;9}Hvad er en model?
En model er din C#-klasse, der afspejler en tabel i databasen. EF Core map’er egenskaber til kolonner og sørger for, at ændringer i modeller kan omsættes til migrations.
- Hold modeller enkle og uden forretningslogik.
- Validering kan både ligge i API-laget og i databasen.
3) Opret DbContext
csharp · 19 lines
1using Demo.Api.Models;2using Microsoft.EntityFrameworkCore;3 4namespace Demo.Api.Data;5 6public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)7{8 public DbSet<Product> Products => Set<Product>();9 10 protected override void OnModelCreating(ModelBuilder b)11 {12 b.Entity<Product>(e =>13 {14 e.HasKey(x => x.Id);15 e.Property(x => x.Name).HasMaxLength(200).IsRequired();16 e.HasIndex(x => x.Name).IsUnique(false);17 });18 }19}Hvad er DbContext?
DbContext er EF Cores gateway til databasen. Den sporer ændringer, oversætter LINQ til SQL og udfører queries. Hver DbSet<T> svarer typisk til en tabel.
- Brug
OnModelCreatingtil constraints, indexes og relationer. - I web apps er livstiden typisk scoped pr. request.
Ydelse & tracking
- Brug
AsNoTracking()til read-only queries. - EF Core kan batche flere kommandoer ved
SaveChanges.
4) Connection string
json · 5 lines
1{2 "ConnectionStrings": {3 "Default": "Server=(localdb)\\MSSQLLocalDB;Database=DemoApiDb;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"4 }5}Connection strings
Læg connection strings i appsettings.json og læs dem via IConfiguration. Undgå at committe produktionshemmeligheder.
- LocalDB: nemt lokalt.
- TrustServerCertificate: praktisk lokalt, men brug rigtigt certifikat i produktion.
5) Registrér EF Core i Program.cs
csharp · 23 lines
1using Demo.Api.Data;2using Microsoft.EntityFrameworkCore;3 4var builder = WebApplication.CreateBuilder(args);5 6// 1) DbContext7builder.Services.AddDbContext<AppDbContext>(opt =>8 opt.UseSqlServer(builder.Configuration.GetConnectionString("Default")));9 10// 2) Swagger11builder.Services.AddEndpointsApiExplorer();12builder.Services.AddSwaggerGen();13 14var app = builder.Build();15 16if (app.Environment.IsDevelopment())17{18 app.UseSwagger();19 app.UseSwaggerUI();20}21 22app.MapGet("/", () => "EF Core API kører!");23app.Run();DI og livstider
AddDbContext registrerer AppDbContext som scoped. Det betyder én context pr. HTTP request.
- Swagger er godt til dokumentation og test under udvikling.
- Overvej
app.UseHttpsRedirection()og CORS når frontend kobles på.
6) Migration & database
bash · 9 lines
1// generér migration baseret på DbContext + modeller2dotnet ef migrations add InitialCreate --project Demo.Api3 4// anvend migrationer på databasen5dotnet ef database update --project Demo.Api6 7// fremover ved ændringer:8// dotnet ef migrations add AddSomething9// dotnet ef database updateHvad er en migration?
En migration er en versioneret ændring i databaseskemaet. Den beskriver, hvordan databasen skal ændres fra én tilstand til en anden.
- Kør
addnår modeller ændrer sig. - Kør
updatefor at anvende ændringerne.
Typiske problemer
- “Unable to create an object of type AppDbContext”: tjek Program.cs setup eller design-time factory.
- Brug
--startup-projecthvis du har flere projekter.
7) Seed dev data
csharp · 16 lines
1// ...efter var app = builder.Build();2using (var scope = app.Services.CreateScope())3{4 var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();5 6 if (!db.Products.Any())7 {8 db.Products.AddRange(9 new() { Name = "Coffee Beans", Price = 79.95m, IsActive = true },10 new() { Name = "Mechanical Keyboard", Price = 799.00m, IsActive = true },11 new() { Name = "USB-C Cable", Price = 49.00m, IsActive = true }12 );13 14 db.SaveChanges();15 }16}Hvorfor seede?
Seed giver dig realistiske testdata med det samme. Hold dev-seed adskilt fra produktion.
- Brug små datasæt, der er gode nok til test.
- Brug
HasData()til statiske opslagsværdier.
8) API-endpoints
csharp · 52 lines
1app.MapGroup("/api/products").WithTags("Products").MapProductsApi();2 3public static class ProductsApi4{5 public static RouteGroupBuilder MapProductsApi(this RouteGroupBuilder group)6 {7 group.MapGet("/", async (AppDbContext db) =>8 await db.Products.Where(p => p.IsActive).ToListAsync());9 10 group.MapGet("/{id:int}", async (int id, AppDbContext db) =>11 await db.Products.FindAsync(id) is { } p12 ? Results.Ok(p)13 : Results.NotFound());14 15 group.MapPost("/", async (Product dto, AppDbContext db) =>16 {17 db.Products.Add(dto);18 await db.SaveChangesAsync();19 20 return Results.Created($"/api/products/{dto.Id}", dto);21 });22 23 group.MapPut("/{id:int}", async (int id, Product dto, AppDbContext db) =>24 {25 var p = await db.Products.FindAsync(id);26 27 if (p is null) return Results.NotFound();28 29 p.Name = dto.Name;30 p.Price = dto.Price;31 p.IsActive = dto.IsActive;32 33 await db.SaveChangesAsync();34 35 return Results.NoContent();36 });37 38 group.MapDelete("/{id:int}", async (int id, AppDbContext db) =>39 {40 var p = await db.Products.FindAsync(id);41 42 if (p is null) return Results.NotFound();43 44 db.Products.Remove(p);45 await db.SaveChangesAsync();46 47 return Results.NoContent();48 });49 50 return group;51 }52}Minimal API vs Controllers
Minimal API er god til små og mellemstore services. Controllers giver mere struktur i større projekter.
- Brug
MapGroupogWithTagsfor pæn Swagger. - Brug DTO’er til input og output i rigtige projekter.
9) Repository-lag
csharp · 10 lines
1using Demo.Api.Models;2 3public interface IProductsRepository4{5 Task<List<Product>> GetAllAsync();6 Task<Product?> GetByIdAsync(int id);7 Task<Product> CreateAsync(Product p);8 Task UpdateAsync(Product p);9 Task DeleteAsync(Product p);10}csharp · 32 lines
1using Demo.Api.Data;2using Demo.Api.Models;3using Microsoft.EntityFrameworkCore;4 5public class ProductsRepository(AppDbContext db) : IProductsRepository6{7 public Task<List<Product>> GetAllAsync()8 => db.Products.Where(p => p.IsActive).ToListAsync();9 10 public Task<Product?> GetByIdAsync(int id)11 => db.Products.FirstOrDefaultAsync(p => p.Id == id);12 13 public async Task<Product> CreateAsync(Product p)14 {15 db.Products.Add(p);16 await db.SaveChangesAsync();17 18 return p;19 }20 21 public async Task UpdateAsync(Product p)22 {23 db.Products.Update(p);24 await db.SaveChangesAsync();25 }26 27 public async Task DeleteAsync(Product p)28 {29 db.Products.Remove(p);30 await db.SaveChangesAsync();31 }32}csharp · 1 lines
1builder.Services.AddScoped<IProductsRepository, ProductsRepository>();Hvorfor et repository-lag?
Et repository isolerer dataadgang, så endpoints forbliver tynde og nemmere at teste.
- Brug repositories til domæneorienterede metoder.
- Undgå bare at kopiere
DbSet1:1.
10) EF Core CLI – hurtig oversigt
bash · 11 lines
1// Ny migration ud fra dine modelændringer2dotnet ef migrations add AddNewFields --project Demo.Api3 4// Anvend migrationer på databasen5dotnet ef database update --project Demo.Api6 7// Gå tilbage til en specifik migration8dotnet ef database update InitialCreate --project Demo.Api9 10// Fjern seneste migration, hvis den ikke er applied11dotnet ef migrations remove --project Demo.ApiOverblik
Det normale workflow er: add → update → eventuelt remove hvis noget skal laves om.
Tip: SQLite
- Erstat
Microsoft.EntityFrameworkCore.SqlServer - Med
Microsoft.EntityFrameworkCore.Sqlite - Skift til
opt.UseSqlite(...)iProgram.cs