ITB.CQRS
2.1.0
dotnet add package ITB.CQRS --version 2.1.0
NuGet\Install-Package ITB.CQRS -Version 2.1.0
<PackageReference Include="ITB.CQRS" Version="2.1.0" />
<PackageVersion Include="ITB.CQRS" Version="2.1.0" />
<PackageReference Include="ITB.CQRS" />
paket add ITB.CQRS --version 2.1.0
#r "nuget: ITB.CQRS, 2.1.0"
#:package ITB.CQRS@2.1.0
#addin nuget:?package=ITB.CQRS&version=2.1.0
#tool nuget:?package=ITB.CQRS&version=2.1.0
ITB.CQRS
CQRS, Repository and Specification building blocks for .NET 10 applications on Entity Framework Core.
| Package | Namespace | What it contains |
|---|---|---|
ITB.CQRS |
ITB.CQRS |
Commands, queries, handlers, IHandlerDispatcher, decorators, filtered/paged list queries |
ITB.Repository.Abstraction |
ITB.Repository.Abstraction |
IReadRepository<T>, IRepository<T> |
ITB.Repository.EntityFrameworkCore |
ITB.Repository.EntityFrameworkCore |
EF Core repositories and IUnitOfWork |
ITB.Specification |
ITB.Specification |
Specification pattern with &, |, ! composition and includes |
ITB.ResultModel |
ITB.Shared.Result |
Result, Result<T> and the Failure hierarchy |
ITB.Shared.Domain |
ITB.Domain.Entities, ITB.Domain.Interfaces |
IEntity, Entity<TKey>, IQueryableFilter<T> |
Getting started
dotnet add package ITB.CQRS
dotnet add package ITB.Repository.EntityFrameworkCore
builder.Services.AddDbContext<AppDbContext>(...);
// Repositories and the transaction decorator resolve the base DbContext type.
builder.Services.AddScoped<DbContext>(sp => sp.GetRequiredService<AppDbContext>());
builder.Services.AddRepositories();
builder.Services.AddQueryableFilters(typeof(Program).Assembly);
builder.Services.AddSpecificationFactory();
builder.Services.AddCQRS([typeof(Program).Assembly]);
AddCQRS scans the given assemblies for handlers, FluentValidation validators, IPermissionValidator<T>,
IAccessFilter<T> and Mapster configurations.
Commands and queries
public class CreateClient : CommandBase<int>
{
public string Name { get; set; }
}
public class CreateClientHandler(IRepository<Client> clients, IUnitOfWork unitOfWork)
: CommandHandlerBase<CreateClient, int>
{
public override async Task<Result<int>> Handle(CreateClient input)
{
var client = await clients.Add(new Client(input.Name));
await unitOfWork.SaveChanges();
return client.Id;
}
}
// Controller / endpoint
var result = await dispatcher.Handle<CreateClient, int>(command);
Every handler is wrapped by decorators, outermost first:
- ErrorHandlerDecorator turns an unhandled exception into an
ExceptionFailure(configurable throughCQRSOptions.ExceptionHandler). - PermissionValidationHandlerDecorator runs every
IPermissionValidator<TIn>and returnsForbiddenFailureon failure. - ValidationHandlerDecorator runs every FluentValidation
IValidator<TIn>and returnsValidationFailure. - TransactionHandlerDecorator wraps commands (
CommandBase) in a database transaction inside the EF execution strategy and commits only on success. Mark a command with[IgnoreTransaction]to opt out.
Filtered lists
Derive the query from GetFilteredListQuery<TOut> and the handler from GetFilteredListQueryHandler<TIn, TOut, TEntity>.
Override CheckAccess, BuildSpecification, BuildBaseQuery, Search, Sort or GetItems as needed; the result
is projected to TOut with Mapster and returned as PagedList<TOut> with the total count.
Helpers:
FilterValidationExtensions.MustBeValidSortPath(...)rejects unknown sort columns in a validator.LikePatterns.Contains(value)escapes%,_and\forEF.Functions.Like(..., LikePatterns.EscapeChar).
Migrating from 1.x
2.0 is a breaking release:
- Target framework is net10.0.
- SimpleInjector was replaced by Microsoft.Extensions.DependencyInjection (with Scrutor).
AddCQRSno longer takes aContainer. - AutoMapper was replaced by Mapster.
GetFilteredListQueryHandlerno longer takes anIMapper. - Handler contracts dropped the
Task<Result<...>>wrapper from the generic argument:ICommand<TOut>/IHandler<TIn, TOut>now returnTask<Result<TOut>>, and result-less commands useICommand/IHandler<TIn>.HandlerBasewas removed. SortOrderis now an enum;Sorting.Pathno longer parses.Asc/.Descsuffixes.PagedList<T>.Countisint(always populated) instead ofint?.ApplyPageFilternow defaults to 10 000 items when no page size is given, and clamps negative sizes and overflowing offsets.AddRepositories()returnsIServiceCollection;RepositoryBuilderwas removed andAddQueryableFiltersis an extension onIServiceCollection.- Namespaces:
ITB.ResultModel→ITB.Shared.Result,ITB.Shared.Domain→ITB.Domain.Entities/ITB.Domain.Interfaces. - The previously separate repositories (
ITB.Repository,ITB.Specification,ITB.Shared) now live here.
Releasing
Packages are published by pushing a version tag:
git tag v2.0.0 && git push origin v2.0.0
A tag with a prerelease suffix (v2.0.0-beta.1) publishes prerelease packages.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FluentValidation (>= 12.1.1)
- ITB.Repository.Abstraction (>= 2.1.0)
- ITB.ResultModel (>= 2.1.0)
- ITB.Shared.Domain (>= 2.1.0)
- ITB.Specification (>= 2.1.0)
- Mapster (>= 10.0.7)
- Mapster.DependencyInjection (>= 10.0.7)
- Mapster.EFCore (>= 10.0.7)
- Microsoft.EntityFrameworkCore (>= 10.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- Scrutor (>= 7.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.