.NET 6 Minimal API Entity Framework
Watch the video here
Prerequisites
Loose Agenda
- Introduce CRUD repositories with EF Core in .NET 6 preview 7
Step by Step
Procure a Database
We’re going to need a database for today’s efforts, so lets leverage an existing docker compose definition to procure ourselves a postgres instance.
I’ve prepared a docker compose definition at the Non-Zero Days wiki-toxicity-database repository which will spin us up a database for today’s exercise.
Clone down that repository, navigate to it in a terminal and run docker compose up -d
to obtain our database.
Setup Playground
Next, let’s create a directory for today’s exercise and navigate to it in a terminal instance.
Install EF dotnet CLI tools
In order to scaffold the database (generate Entity Framework code) in the webapi project you must first install the Entity Framework .NET CLI tools. dotnet tool install --global dotnet-ef
Spin up a new application
Run dotnet new webapi
then open the directory in Visual Studio Code.
Initialize User Secrets
In order to configure our local development environment to connect to the database, we need to run the following
dotnet user-secrets init
dotnet user-secrets set ConnectionStrings:ToxicityDb "Username=docker;Password=docker;Host=host.docker.internal;Database=toxicity;"
Add NuGet Packages
The following commands will add some necessary dependencies for data access with EF and Postgres.
dotnet add .\net-6-ef.csproj package Microsoft.EntityFrameworkCore.Design
dotnet add .\net-6-ef.csproj package Npgsql.EntityFrameworkCore.PostgreSQL
Scaffold Database Access
Now we can generate the Entity Framework code.
dotnet ef dbcontext scaffold "Server=localhost;Database=toxicity;Port=5432;Username=docker;Password=docker;" Npgsql.EntityFrameworkCore.PostgreSQL --output-dir "Infrastructure"
Configure Program.cs
We’ll start by grabbing our configured connection string and adding the DbContext with that configuration.
var connectionString = builder.Configuration.GetConnectionString("ToxicityDb");
builder.Services.AddDbContext<toxicityContext>(options => options.UseNpgsql(connectionString));
Replace builder.Services.AddControllers();
with builder.Services.AddEndpointsApiExplorer();
Move these two lines out of the if statement
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "net_6_ef v1"));
Remove these two lines
app.UseAuthorization();
app.MapControllers();
Expose Data Access GET
Now we can add an endpoint. Just above app.Run();
let’s add the following code:
app.MapGet("/toxicity", async (toxicityContext _dbContext) => {
return await _dbContext.ToxicityAnnotations.ToListAsync();
});
Now let’s run the application via dotnet run
and navigate to https://localhost:5001/toxicity to see the data from our database.
Expose Data Access POST
For a POST endpoint we’ll add a paramter to the RequestDelegate for the body of the request and we’ll call AddAsync:
app.MapPost("/toxicity", async (toxicityContext _dbContext, ToxicityAnnotation input) => {
await _dbContext.ToxicityAnnotations.AddAsync(input);
await _dbContext.SaveChangesAsync();
return input;
});
Expose Data Access PUT
PUT is traditionally an idempotent save, therefore we’ll check if an entity exists before doing an add or an update.
app.MapPut("/toxicity", async (toxicityContext _dbContext, ToxicityAnnotation input) => {
var entity = await _dbContext.ToxicityAnnotations.FindAsync(input.RevId, input.WorkerId);
if (entity == null)
{
await _dbContext.ToxicityAnnotations.AddAsync(input);
await _dbContext.SaveChangesAsync();
return input;
}
Console.WriteLine("Updating");
_dbContext.Entry(entity).CurrentValues.SetValues(input);
await _dbContext.SaveChangesAsync();
return input;
});
Expose Data Access DELETE
DELETE will check if the entity exists and remove it if it does
app.MapDelete("/toxicity", async (toxicityContext _dbContext, decimal revId, decimal workerId) => {
var entity = await _dbContext.ToxicityAnnotations.FindAsync(revId, workerId);
if (entity == null)
{
return false;
}
_dbContext.ToxicityAnnotations.Remove(entity);
await _dbContext.SaveChangesAsync();
return true;
});
Let’s run the application via dotnet run
and navigate to Swagger to play with our endpoints.
Additional Resources
Congratulations on a non-zero day!