Notes on .NET 6 features (#6)

- blazor components
- `field` keyword
- with-expression for anonymous types
- namespace semplification
- lambda signature inference
- static methods and properties on interfaces
- .NET 6 LINQ improvements
- global usings notes
- null parameter checking
- required properties
- native memory allocation
- Minimal APIs
- net 6 implicit imports
This commit is contained in:
Marcello 2021-10-04 22:37:01 +02:00 committed by GitHub
parent 56a963bec8
commit b2abff42c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 787 additions and 323 deletions

View file

@ -1,5 +1,181 @@
# ASP .NET REST API
## Startup class
- Called by `Program.cs`
```cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace <Namespace>
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); // controllers w/o views
//or
services.AddControllersWithViews(); // MVC Controllers
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
```
## DB Context (EF to access DB)
NuGet Packages to install:
- `Microsoft.EntityFrameworkCore`
- `Microsoft.EntityFrameworkCore.Tools`
- `Microsoft.EntityFrameworkCore.Design` *or* `Microsoft.EntityFrameworkCore.<db_provider>.Design`
- `Microsoft.EntityFrameworkCore.<db_provider>`
In `AppDbContext.cs`:
```cs
using <Namespace>.Model;
using Microsoft.EntityFrameworkCore;
namespace <Namespace>.Repo
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<ProjectContext> options) : base(options)
{
}
//DBSet<TEntity> represents the collection of all entities in the context, or that can be queried from the database, of a given type
public DbSet<Entity> entities { get; set; }
}
}
```
In `appsettings.json`:
```json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"CommanderConnection" : "Server=<server>;Database=<database>;UID=<user>;Pwd=<password>"
}
}
```
In `Startup.cs`:
```cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// SqlServer is the db used in this example
services.AddDbContext<CommanderContext>(option => option.UseSqlServer(Configuration.GetConnectionString("CommanderConnection")));
services.AddControllers();
}
```
### Migrations
- Mirroring of model in the DB.
- Will create & update DB Schema if necessary
In Package Manager Shell:
```ps1
add-migrations <migration_name>
update-database # use the migrations to modify the db
```
## Repository
In `IEntityRepo`:
```cs
using <Namespace>.Model;
using System.Collections.Generic;
namespace <Namespace>.Repository
{
public interface IEntityRepo
{
IEnumerable<Entity> SelectAll();
Entity SelectOneById(int id);
...
}
}
```
In `EntityRepo`:
```cs
using <Namespace>.Model;
using System.Collections.Generic;
namespace <Namespace>.Repo
{
public class EntityRepo : IEntityRepo
{
private readonly AppDbContext _context;
public EntityRepo(AppDbContext context)
{
_context = context;
}
public IEnumerable<Entity> SelectAll()
{
return _context.Entities.ToList(); // linq query (ToList()) becomes sql query
}
public Entity SelectOneById(int id)
{
return _context.Entities.FirstOrDefault(p => p.Id == id);
}
...
}
}
```
## Data Transfer Objects (DTOs)
A **DTO** is an object that defines how the data will be sent and received over the network (usually as JSON).
@ -7,28 +183,38 @@ Without a DTO the JSON response (or request) could contain irrelevant, wrong or
Moreover, by decoupling the JSON response from the actual data model, it's possible to change the latter without breaking the API.
DTOs must be mapped to the internal methods.
In `EntityDTO.cs`:
Required NuGet Packages:
- AutoMapper.Extensions.Microsoft.DependencyInjection
In `StartUp.cs`:
```cs
using AutoMapper;
// ...
public void ConfigureServices(IServiceCollection services)
{
// other services
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); // set automapper service
}
```
In `Entity<CrudOperation>DTO.cs`:
```cs
namespace <Namespace>.DTOs
{
// define the data to be serialized in JSON (differs from model)
public class EntityDTO
// define the data to be serialized in JSON (can differ from model)
public class EntityCrudOpDTO // e.g: EntityReadDTO, ...
{
// only properties to be serialized
}
}
```
### DTO mapping with Automapper
Required NuGet Packages:
- `AutoMapper`
- `AutoMapper.Extensions.Microsoft.DependencyInjection`
A good way to organize mapping configurations is with *profiles*.
In `EntitiesProfile.cs`:
```cs
@ -42,7 +228,7 @@ namespace <Namespace>.Profiles
{
public EntitiesProfile()
{
CreateMap<Entity, EntityDTO>(); // map entity to it's DTO
CreateMap<Entity, EntityCrudOpDTO>(); // map entity to it's DTO
}
}
}
@ -83,35 +269,68 @@ namespace <App>.Controllers
[ApiController]
public class EntitiesController : ControllerBase // MVC controller w/o view
{
// service or repo (DAL) injection
private readonly ICommandRepo _repo;
private readonly IMapper _mapper; // AutoMapper class
[HttpGet] // GET api/endpoint
public ActionResult<EntityDTO> SelectAllEntities()
public EntitiesController(IEntityRepo repository, IMapper mapper) // injection og the dependency
{
...
_repo = repository;
_mapper = mapper
}
return Ok(_mapper.Map<EntityDTO>(entity));
[HttpGet] // GET api/endpoint
public ActionResult<IEnumerable<EntityCrudOpDTO>> SelectAllEntities()
{
var results = _repo.SelectAll();
return Ok(_mapper.Map<EntityCrudOpDTO>(results)); // return an action result OK (200) with the results
}
// default binding source: [FromRoute]
[HttpGet("{id}")] // GET api/endpoint/{id}
public ActionResult<EntityCrudOpDTO> SelectOneEntityById(int id)
{
var result = _repo.SelectOneById(id);
if(result != null)
{
return Ok(_mapper.Map<EntityCrudOp>(result)); // transform entity to it's DTO
}
return NotFound(); // ActionResult NOT FOUND (404)
}
}
}
```
## Simple API Controller
## Controller (With View)
```cs
[Route("api/endpoint")]
[ApiController]
public class EntitiesController : ControllerBase
{
// service or repo (DAL) injection
using <App>.Model;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
[HttpGet]
public ActionResult<TEntity> SelectAll()
namespace <App>.Controllers
{
[Route("api/endpoint")]
[ApiController]
public class EntitiesController : Controller
{
...
return Ok(entity);
private readonly AppDbContext _db;
public EntitiesController(AppDbContext db)
{
_db = db;
}
[HttpGet]
public IActionResult SelectAll()
{
return Json(new { data = _db.Entities.ToList() }); // json view
}
}
}
```