.Net Core
Quick-starts and guidelines for .Net Core development
Sample Projects
.NET 8.0 gamestoolkit.api
Clean Architecture / Dapper / EFCore / CQRS / AutoMapper / Swagger / Functional testing / Unit testing / CI.
.NET Core 2.2 AspCore.SampleAPI
Clean Architecture / Domain / Behaviors / Queries / AutoMapper / Swagger / Functional testing / CI.
Quick guides/samples
Web API Example
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
protected readonly IUsersRepository _repository;
public UsersController(IUsersRepository repository)
{
_repository = repository;
}
[HttpGet]
[ProducesResponseType(200)]
public async Task<ActionResult<IEnumerable<User>>> GetAllAsync()
{
return await _repository.GetAll();
}
[HttpGet("{username}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<User>> GetByIdAsync(string username)
{
var existingUser = await _repository.GetByUsername(username);
if (existingUser == null)
{
return NotFound();
}
return existingUser;
}
[HttpPost]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
public async Task<ActionResult<User>> CreateUserAsync(User user)
{
await _repository.AddUser(user);
return CreatedAtAction(nameof(GetById), new { username = user.Username }, user);
}
[HttpPut("{username}")]
[ProducesResponseType(204)]
[ProducesResponseType(404)]
public async Task<IActionResult> UpdateUserAsync(string username, User user)
{
var existingUser = await _repository.GetByUsername(username);
if (existingUser == null)
{
return NotFound();
}
existingUser.Nickname = user.Nickname;
existingUser.Email = user.Email;
await _repository.Update(existingUser);
return NoContent();
}
[HttpDelete("{username}")]
[ProducesResponseType(204)]
[ProducesResponseType(404)]
public async Task<IActionResult> DeleteUserAsync(string username)
{
var existingUser = await _repository.GetByUsername(username);
if (existingUser == null)
{
return NotFound();
}
await _repository.Delete(existingUser);
return NoContent();
}
}
Global error handling
For .NET 8:
Add these lines to your Program.cs:
... builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); builder.Services.AddProblemDetails(); ... app.UseExceptionHandler();
Create a ViewModel:
public class ErrorResponseViewModel { public string Message { get; set; } public int StatusCode { get; set; } }
Add and customize the global exception handler:
public class GlobalExceptionHandler : IExceptionHandler { private readonly ILogger _logger; public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) { _logger = logger; } public async ValueTask<bool> TryHandleAsync( HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { _logger.LogError($"Exception [{ exception.Message }]. Inner exception [{ exception.InnerException?.Message}]. Stack trace: [{ exception.StackTrace }]"); var errorResponse = new ErrorResponseViewModel(); switch (exception) { case BadHttpRequestException: errorResponse.StatusCode = (int)HttpStatusCode.BadRequest; errorResponse.Message = exception.GetType().Name; break; default: errorResponse.StatusCode = (int)HttpStatusCode.InternalServerError; errorResponse.Message = "Internal Server Error"; break; } httpContext.Response.StatusCode = errorResponse.StatusCode; await httpContext .Response .WriteAsJsonAsync(errorResponse, cancellationToken); // You may return false and set another ExceptionHandler to handle specific exceptions return true; } }
More info:
EF Core Hotspots
Startup.cs -> Configure Services
services.AddDbContext<MYContext>( options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), serverOptions => serverOptions.MigrationsAssembly("ASSEMBLYNAME")));
appsettings.json
{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=DBNAME;Trusted_Connection=True;MultipleActiveResultSets=true" }, ... }
DbContext:
public class MYContext : DbContext { public MYContext() { } public MYContext(DbContextOptions<MYContext> options) : base(options) { } public DbSet<...> .... }
Testing InMemoryDatabase provider:
new DbContextOptionsBuilder<MYContext>() .UseInMemoryDatabase(databaseName: NAME) .Options
AutoMapper DI
AutoMapper Extensions for Microsoft DI.
// Add this line to your startup.cs
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
// Or this one...
services.AddAutoMapper(typeof(Program))
Mediatr
Install:
dotnet add package MediatR
Injection:
services.AddMediatR(cfg => { cfg.RegisterServicesFromAssembly(typeof(Program).Assembly); });
Request / Response (single handler):
// Request with return public class Command : IRequest<string> // IRequest<ReturnType> { // Add command params here } // Response (note generic params) public class CommandHandler : IRequestHandler<Command, string> // IRequestHandler<Request, ReturnType> { public async Task<string> Handle(Command request, CancellationToken cancellationToken) { // You may need to read your command params // ... // ... return Task.FromResult("Result"); } } // Send message from any other place var command = new Command(); var response = await mediator.Send(Command);
If you do not require a response:
public class OneWay : IRequest { } public class OneWayHandler : IRequestHandler<OneWay> { public async Task Handle(OneWay request, CancellationToken cancellationToken) { // Do work // ... return Task.CompletedTask; } }
Notifications (multiple handlers):
public class MyEvent : INotification { } public class MyEventStore : INotificationHandler<MyEvent> { public async Task Handle(MyEvent notification, CancellationToken cancellationToken) { // Save in your DB // ... return Task.CompletedTask; } } public class MyEventNotify : INotificationHandler<MyEvent> { public async Task Handle(MyEvent notification, CancellationToken cancellationToken) { // Do work // ... return Task.CompletedTask; } }
More info:
Docker development (with Visual Studio)
Install Docker Desktop and configure it for Linux Containers.
Run these commands (you may need to enable CPU virtualization on your BIOS):
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart # If update fails, try to install: wsl.exe --install wsl.exe --update wsl --set-default-version 2
Restart. Accept SSL certificates and Re-restart your machine.
You’re ready to go!
- Problems? Check this guide
Performance
A kind of - Practical intro to Async APIs
- Async discussions:
Use DbContextPooling to improve the performance: .Net Core 2.1
Vortex
Write elegant and testeable solutions on C# using a Monadic Framework. ASP Core joins the functional side!