Not Minimal API |
If you have installed .NET 6 Preview 4 and do dotnet new web, you should see something new.
dotnet new web -n MinimalApi
Program.cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
await using var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapGet("/", (Func<string>)(() => "Hello World!"));
await app.RunAsync();
This is just a simple API, which will return just "Hello World". There is no Main method, it's using Top-level statements (a C# 9.0 feature).
Here you can basically do all the things that you used to do like set up dependencies and configure the HTTP Request Pipeline (what we usually do in ConfigureServices and Configure methods in Startup.cs respectively).
To give an example, something like this.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// ConfigureServices
builder.Services.AddDbContext<EmployeeContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
await using WebApplication app = builder.Build();
// Configure
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} // Setup routes and run the app
Here, I have set up a EF DbContext within the container and in the HTTP Request Pipeline, have set up a DeveloperExceptionPage if the environment is Development (again we used to do this in ConfigureServices and Configure methods in Startup.cs respectively).
Consider the below EmployeeContext.
namespace MinimalApi
{
public class EmployeeContext : DbContext
{
public EmployeeContext(DbContextOptions options) : base(options)
{
}
public DbSet<Employee> Employees { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
}
I can basically create a CRUD API for Employees here easily, something like below.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// ConfigureServices
builder.Services.AddDbContext<EmployeeContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
await using WebApplication app = builder.Build();
// Configure
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapGet("/employees", async ([FromServices] EmployeeContext dbContext) =>
{
return await dbContext.Employees.ToListAsync();
});
app.MapGet("/employees/{id}", async ([FromServices] EmployeeContext dbContext, int id) =>
{
Employee employee = await dbContext.Employees.FindAsync(id);
if (employee is null)
{
return NotFound();
}
return Ok(employee);
});
app.MapPost("/employees", async ([FromServices] EmployeeContext dbContext, Employee employee) =>
{
await dbContext.Employees.AddAsync(employee);
await dbContext.SaveChangesAsync();
return Ok(employee);
});
app.MapPut("/employees/{id}", async ([FromServices] EmployeeContext dbContext, int id, Employee employee) =>
{
if (id != employee.Id)
{
return BadRequest();
}
if (!await dbContext.Employees.AnyAsync(x => x.Id == id))
{
return NotFound();
}
dbContext.Entry(employee).State = EntityState.Modified;
await dbContext.SaveChangesAsync();
return NoContent();
});
app.MapDelete("/employees/{id}", async ([FromServices] EmployeeContext dbContext, int id) =>
{
Employee employee = await dbContext.Employees.FindAsync(id);
if (employee is null)
{
return NotFound();
}
dbContext.Employees.Remove(employee);
await dbContext.SaveChangesAsync();
return NoContent();
});
await app.RunAsync();
There is an interesting thing here. That is from the endpoints, all my return types are custom types that implement IResult, a new type that is coming with .NET 6.
My Return types are mapped manually here, through this class ResultMapper class.
public static class ResultMapper
{
public static IResult BadRequest() => new StatusCodeResult(StatusCodes.Status400BadRequest);
public static IResult NotFound() => new StatusCodeResult(StatusCodes.Status404NotFound);
public static IResult NoContent() => new StatusCodeResult(StatusCodes.Status204NoContent);
public static OkResult<T> Ok<T>(T value) => new(value);
public class OkResult<T> : IResult
{
private readonly T _value;
public OkResult(T value)
{
_value = value;
}
public Task ExecuteAsync(HttpContext httpContext)
{
return httpContext.Response.WriteAsJsonAsync(_value);
}
}
}
Hopefully, we will not have to do this in the next releases.
So as developers or newbies, we can start from the Minimal APIs and we can grow as we go rather than having a somewhat complex structure from the first day. Microsoft is going to provide tooling (I believe through Visual Studio and VS Code), so we can refactor the code into the current structure (separate Controller etc) as the code grows. An important thing is, this is not going to replace the existing code structure, it's just we can start with as little code as possible and then grow as we go!
You can find the sample code here,
https://github.com/jaliyaudagedara/minimal-api
In this sample code, you will see I am using global usings (a C# 10 feature), you can read more about that here: C# 10.0: Introducing Global Usings
Hope this helps.
Happy Coding.
Regards,
Jaliya
No comments:
Post a Comment