Modern
.NET modern tools for fast and efficient development
Install / Use
/learn @anton-martyniuk/ModernREADME
Modern
What is Modern?
Modern is a set of modern .NET tools :hammer: :wrench: for fast and efficient development of common backend tasks.
It allows to create a production ready applications with just set of models and configuration which can be further extended.
Modern tool are flexible, easily changeable and extendable.
It includes the following components:
- generic repositories for SQL and NoSQL databases
- generic services with and without caching support
- generic in memory services with in-memory filtering capabilities
- in-memory and redis generic caches
- generic set of CQRS queries and commands over repository (if you prefer CQRS over services)
- generic controllers for all types of services
- OData controllers for all types of services
For more information - see full documentation here.
Examples for all types of components - see here.
Table of contents :bookmark_tabs:
- How to get started?
- Roadmap
- Repositories
- Repositories for SQL databases
- Repositories for NoSQL databases
- Services
- Services with caching
- Services In Memory
- CQRS
- CQRS with caching
- Controllers
- Controllers CQRS
- Controllers In Memory
- OData Controllers
- OData Controllers In Memory
How to get started?
Lets create a Web Api with CRUD operations over Airplane entities. We need a repository, service and controller.
- Install the following Nuget packages:
-
Create classes for the following models: AirplaneDto and AirplaneDbo.
-
Create an EF Core DbContext for accessing the AirplaneDbo.
-
Register the Modern builder in DI and add the following components:
builder.Services
.AddModern()
.AddRepositoriesEfCore(options =>
{
options.AddRepository<FlyingDbContext, AirplaneDbo, long>();
})
.AddServices(options =>
{
options.AddService<AirplaneDto, AirplaneDbo, long, IModernRepository<AirplaneDbo, long>>();
})
.AddControllers(options =>
{
options.AddController<CreateRequest, UpdateRequest, AirplaneDto, AirplaneDbo, long>();
});
As a result a production ready API will be created:
GET :large_blue_circle:
/Airplanes/get/{id}
GET :large_blue_circle: :large_blue_circle:
/Airplanes/get
POST :white_check_mark:
/Airplanes/create
POST :white_check_mark: :white_check_mark:
/Airplanes/create-many
PUT :part_alternation_mark:
/Airplanes/update/{id}
PUT :part_alternation_mark: :part_alternation_mark:
/Airplanes/update-many
PATCH :heavy_dollar_sign:
/Airplanes/patch/{id}
DELETE :x:
/Airplanes/delete/{id}
DELETE :x: :x:
/Airplanes/delete-many
Roadmap :arrow_right: :date:
The following features will be implemented in the next releases:
- Assembly scan in DI packages
- Unit and integration tests
- MinimalsApis
- Reflection improvements
Repositories
Modern generic repository is divided into 2 interfaces: IModernQueryRepository<TEntity, TId> and IModernCrudRepository<TEntity, TId>.
IModernQueryRepository has the following methods:
Task<TEntity> GetByIdAsync(TId id, EntityIncludeQuery<TEntity>? includeQuery = null, CancellationToken cancellationToken = default);
Task<TEntity?> TryGetByIdAsync(TId id, EntityIncludeQuery<TEntity>? includeQuery = null, CancellationToken cancellationToken = default);
Task<IEnumerable<TEntity>> GetAllAsync(EntityIncludeQuery<TEntity>? includeQuery = null, CancellationToken cancellationToken = default);
Task<long> CountAsync(CancellationToken cancellationToken = default);
Task<long> CountAsync(Expression<Func<TEntity, bool>> predicate, EntityIncludeQuery<TEntity>? includeQuery = null,
CancellationToken cancellationToken = default);
Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> predicate, EntityIncludeQuery<TEntity>? includeQuery = null, CancellationToken cancellationToken = default);
Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, EntityIncludeQuery<TEntity>? includeQuery = null, CancellationToken cancellationToken = default);
Task<TEntity?> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, EntityIncludeQuery<TEntity>? includeQuery = null, CancellationToken cancellationToken = default);
Task<IEnumerable<TEntity>> WhereAsync(Expression<Func<TEntity, bool>> predicate, EntityIncludeQuery<TEntity>? includeQuery = null, CancellationToken cancellationToken = default);
Task<PagedResult<TEntity>> WhereAsync(Expression<Func<TEntity, bool>> predicate, int pageNumber, int pageSize, EntityIncludeQuery<TEntity>? includeQuery = null, CancellationToken cancellationToken = default);
IQueryable<TEntity> AsQueryable();
IModernCrudRepository has the following methods:
Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancellationToken = default);
Task<List<TEntity>> CreateAsync(List<TEntity> entities, CancellationToken cancellationToken = default);
Task<TEntity> UpdateAsync(TId id, TEntity entity, CancellationToken cancellationToken = default);
Task<List<TEntity>> UpdateAsync(List<TEntity> entities, CancellationToken cancellationToken = default);
Task<TEntity> UpdateAsync(TId id, Action<TEntity> update, CancellationToken cancellationToken = default);
Task<bool> DeleteAsync(TId id, CancellationToken cancellationToken = default);
Task<bool> DeleteAsync(List<TId> ids, CancellationToken cancellationToken = default);
Task<TEntity> DeleteAndReturnAsync(TId id, CancellationToken cancellationToken = default);
Repositories for SQL databases :pencil:
Modern generic repositories for SQL databases are built on top of 2 the following ORM frameworks:
- EF Core
- Dapper
To use EF Core repository install the Modern.Repositories.EFCore Nuget package and register it within Modern builder in DI:
builder.Services
.AddModern()
.AddRepositoriesEfCore(options =>
{
options.AddRepository<FlyingDbContext, AirplaneDbo, long>(useDbFactory: false);
});
Specify the type of EF Core DbContext, Dbo entity model and primary key.
useDbFactory parameter indicates whether repository with DbContextFactory should be used. The default value is false.
:information_source: Use this parameter if you plan to inherit from this generic repository and extend or change its functionality.
When usingDbContextFactoryevery repository creates and closes a database connection in each method.
When NOT usingDbContextFactoryrepository shares the same database connection during its lifetime.
:warning: It is not recommended to use
useDbFactory = falsewhen repository is registered as SingleInstance, otherwise a single database connection will persist during the whole application lifetime
To use Dapper repository install the Modern.Repositories.Dapper Nuget package and register it within Modern builder in DI:
builder.Services
.AddModern()
.AddRepositoriesDapper(options =>
{
options.ProvideDatabaseConnection(() => new NpgsqlConnection(connectionString));
options.AddRepository<AirplaneDapperMapping, AirplaneDbo, long>();
});
Specify the type of Dapper mapping class, Dbo entity model and primary key.
A dapper needs to know how to create a database connection.
Since there are multiple database connection classes - provide the needed one using ProvideDatabaseConnection method.
Dapper repository requires to have a small mapping class that way generic repository can match the entity property name with database table column.
:information_source: Mapping class for Dapper is a part of Modern tools and not a part of Dapper library
For example consider the following mapping class:
public class AirplaneDapperMapping : DapperEntityMapping<AirplaneDbo>
{
protected override void CreateMapping()
{
Table("db_schema_name.airplanes")
.Id(nameof(AirplaneDbo.Id), "id")
.Column(nameof(AirplaneDbo.YearOfManufacture), "year_of_manufacture")
.Column(nameof(AirplaneDbo.ModelId), "model_id")
//...
;
}
}
Repositories for No SQL databases :pencil:
Modern generic repositories for No SQL databases are built on of the following NoSQL databases:
- MongoDB
- LiteDb (embedded single-file NoSQL database)
To use MongoDB repository install the Modern.Repositories.MongoDB Nuget package and register it within Modern builder in DI:
builder.Services
.AddModern()
.AddRepositoriesMongoDb(options =>
{
options.ConfigureMongoDbClient(mongoDbConnectionString);
options.AddRepository<AirplaneDbo, string>("database_name", "collection_name");
});
Specify the type of Dbo entity model and "_id" key.
Provide the connection string in ConfigureMongoDbClient method.
You can also use the second parameter updateSettings and configure the custom parameters in a MongoClientSettings class of MongoDB Driver.
To use LiteDB repository install the Modern.Repositories.LiteDB Nuget package and register it within Modern builder in DI:
builder.Services
.AddModern()
.AddRepositoriesLiteDb(options =>
{
options.AddRepository<AirplaneDbo, string>("connection_string", "collection_name");
});
Specify the type of Dbo entity model and "_id" key.
:information_
