Software-Engineering
# Why Layers ?
Also see CSharp_EF-Core-Definition (Layers / Tiers)
Organizing code into layers is a common practice to promote maintainability, scalability & separation of concerns.
The use of layers like API, Domain, Model helps in structuring the app.
# Breakdown
# Model
This layer manages configuration, entities, migrations.
Configuration > DbContext
Entities > Classes
Migrations > Db-Migrations
# Domain
This layer is connected to the model using the DbContext.
In this layer we can find Repositories
Repositories
Implementation > FirstRepository, SecondRespository, …
Interfaces > IRepositoryBase
# Domain Interfaces
// IRepositoryBase.cs
public interface IRepositoryBase < TEntity , TId >
: where TEntity : class
{
Task < TEntity > CreateAsync ( TEntity t );
Task < List < TEntity >> ReadAllAsync ();
Task < List < TEntity >> ReadAsync ( Expression < Func < TEntity , bool >> filter , int start , int count );
Task < TEntity ?> ReadIdAsync ( TId id );
Task UpdateAsync ( TEntity t );
Task DeleteAsync ( TEntity t );
}
# Domain Implementation
// RepositoryBase.cs
public class RepositoryBase < TEntity , TId >
: IRepositoryBase < TEntity , TId > : where TEntity : class
{
protected readonly YourDbContext Context ;
protected readonly DbSet < TEntity > Table ;
protected RepositoryBase ( YourDbContext context )
{
Context = context;
Table = context. Set < TEntity >();
}
public async Task < TEntity > CreateAsync ( TEntity t )
{
await Table. AddAsync (t);
await Context. SaveChangesAsync ();
return t;
}
public async Task < List < TEntity >> ReadAllAsync () => await Table. ToListAsync ();
public async Task < List < TEntity >> ReadAsync ( Expression < Func < TEntity , bool >> filter , int start int count )
{
return await Table
. Where (filter)
. Skip (start)
. Take (count)
. ToListAsync ();
}
public async Task < TEntity ?> ReadIdAsync ( TId id )
{
return await Table. FindAsync (id);
}
public async Task UpdateAsync ( TEntity t )
{
Table. Update (t);
await Context. SaveChangesAsync ();
}
public async Task DeleteAsync ( TEntity t )
{
Table. Remove (t);
await Context. SaveChangesAsync ();
}
}
Implementierung: TeacherRepository
neue Methode (Konstrukturaufruf)
// TeacherRepository.cs
public class TeacherRepository ( YourDbContext context )
: RepositoryBase < Teacher , int >( context );
alte Methode (Konstrukturaufruf)
// TeacherRepository.cs
public class TeacherRepository
: RepositoryBase < Teacher , int >
{
public TeacherRepository ( TimetableDbContext context )
: base (context) { }
}
# API
This layer is responsible for assigning data.
For assigning data we use Api-Controller
Controllers > GenericController, FirstController, …
# API Controller
Registration
// Program.cs
.. .
builder.Services. AddDbContextFactory < YourDbContext >(
options => options. UseNpgsql (
builder.Configuration. GetConnectionString ( "Default" ))
);
builder.Services. AddScoped < IRepository < Teacher , int >, TeacherRepository >();
builder.Services. AddControllers ();
.. .
app. MapControllers ();
.. .
// GenericController.cs
public abstract class GenericController < TEntity , TId >( IRepositoryBase < TEntity , TId > repository )
: ControllerBase where TEntity : class
{
protected IRepository < TEntity , TId > Repository =
repository ?? throw new ArgumentNullException ( nameof (repository));
[ HttpPost ]
public async Task < ActionResult < TEntity >> Post ( TEntity item )
{
await Repository. CreateAsync (item);
return CreatedAtAction ( nameof (Get), item);
}
[ HttpGet ]
public async Task < ActionResult < List < TEntity >>> Get ()
{
return await Repository. ReadAllAsync ();
}
[ HttpGet ( "{id}" )]
public async Task< ActionResult < TEntity ?> GetById ( TId id )
{
var result = await Repository. ReadIdAsync (id);
return result != null ? Ok (result) : null ;
}
[ HttpPut ( "{id}" )]
public async Task < ActionResult > Put ( TEntity item , TId id )
{
var result = await Repository. ReadIdAsync (id);
if (result is null )
{
return NotFound ();
}
await Repository. UpdateAsync (item);
return NoContent ();
}
[ HttpDelete ( "{id}" )]
public async Task < ActionResult > Delete ( TId id )
{
var result = Repository. ReadIdAsync (id);
if (result is null )
{
return NotFound ();
}
await Repository. DeleteAsync (result);
return Ok ();
}
}
// TeacherController.cs
[ Route ( "[controller]" )]
[ ApiController ]
public class TeacherController ( IRepository < Teacher , int > repository )
: GenericController < Teacher , int >( repository )