Proxos 1.0.2
dotnet add package Proxos --version 1.0.2
NuGet\Install-Package Proxos -Version 1.0.2
<PackageReference Include="Proxos" Version="1.0.2" />
<PackageVersion Include="Proxos" Version="1.0.2" />
<PackageReference Include="Proxos" />
paket add Proxos --version 1.0.2
#r "nuget: Proxos, 1.0.2"
#:package Proxos@1.0.2
#addin nuget:?package=Proxos&version=1.0.2
#tool nuget:?package=Proxos&version=1.0.2
Proxos
MIT-licensed mediator for .NET 8, 9 and 10 — compile-time safety via Source Generator and Roslyn Analyzer, built-in OpenTelemetry, API compatible with MediatR.
Por que Proxos?
| Aspecto | MediatR | Proxos |
|---|---|---|
| Dispatch | Assembly scanning + reflection no startup | Source Generator gera tabela de dispatch em compile-time — zero reflection |
| Startup | Lento — resolve handlers via reflection na primeira chamada | Instantâneo — registro estático gerado em compile-time |
| Handler não encontrado | Exceção em runtime, mensagem genérica | Analyzer PRX001 alerta em compile-time |
| Ordem de behaviors | Depende da ordem de registro — frágil | Determinística e explícita |
| Behaviors condicionais | Apenas via generic constraint | IConditionalBehavior para condições em runtime |
| Timeout por request | Manual | [RequestTimeout(ms)] declarativo |
| Ignorar behavior | Não existe | [IgnoreBehavior(typeof(...))] por request |
| Diagnósticos | Nenhum built-in | ActivitySource + Meter integrados (OpenTelemetry) |
| Contexto no pipeline | Não existe | IPipelineContextAccessor injetável |
| Testes | Requer mocks verbosos | FakeMediator com API fluente |
| Exception side-effects | IRequestExceptionAction (sempre relança) |
✅ igual |
| Licença | Comercial a partir da v13 | MIT |
Instalação
dotnet add package Proxos
dotnet add package Proxos.Testing # apenas em projetos de teste
Configuração
builder.Services.AddProxos(cfg => cfg
.RegisterServicesFromAssembly(typeof(Program).Assembly)
.AddOpenBehavior(typeof(LoggingBehavior<,>)));
Uso
Request com retorno
public record GetUserQuery(int Id) : IRequest<User>;
public class GetUserQueryHandler : IRequestHandler<GetUserQuery, User>
{
public Task<User> Handle(GetUserQuery request, CancellationToken ct)
=> _db.Users.FindAsync(request.Id, ct);
}
// Envio
var user = await mediator.Send(new GetUserQuery(42));
Command (sem retorno)
public record CreateUserCommand(string Name) : IRequest;
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand>
{
public async Task<Unit> Handle(CreateUserCommand request, CancellationToken ct)
{
await _db.Users.AddAsync(new User(request.Name), ct);
return Unit.Value;
}
}
Notificações
public record UserCreated(int UserId) : INotification;
// Múltiplos handlers — todos recebem a notificação
public class SendWelcomeEmail : INotificationHandler<UserCreated> { ... }
public class UpdateAuditLog : INotificationHandler<UserCreated> { ... }
await mediator.Publish(new UserCreated(userId));
Streams
public record GetOrdersQuery(int CustomerId) : IStreamRequest<Order>;
public class GetOrdersHandler : IStreamRequestHandler<GetOrdersQuery, Order>
{
public async IAsyncEnumerable<Order> Handle(GetOrdersQuery request, CancellationToken ct)
{
await foreach (var order in _db.GetOrdersAsync(request.CustomerId, ct))
yield return order;
}
}
await foreach (var order in mediator.CreateStream(new GetOrdersQuery(customerId)))
Process(order);
Pipeline Behaviors
public class LoggingBehavior<TRequest, TResponse>(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
: IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
logger.LogInformation("Handling {Request}", typeof(TRequest).Name);
var response = await next();
logger.LogInformation("Handled {Request}", typeof(TRequest).Name);
return response;
}
}
// Registro
cfg.AddOpenBehavior(typeof(LoggingBehavior<,>));
Behavior Condicional
public class CacheBehavior<TRequest, TResponse>(ICache cache)
: IConditionalBehavior<TRequest, TResponse>
where TRequest : ICacheableRequest
{
// Se retornar false, o behavior é pulado automaticamente — sem overhead
public bool ShouldHandle(TRequest request) => !request.BypassCache;
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
if (cache.TryGet(request.CacheKey, out TResponse? cached)) return cached!;
var result = await next();
cache.Set(request.CacheKey, result);
return result;
}
}
Atributos
Timeout automático
[RequestTimeout(5000)] // cancela automaticamente após 5 segundos
public record GetReportQuery : IRequest<Report>;
Ignorar behavior por request
[IgnoreBehavior(typeof(LoggingBehavior<,>))]
public record HealthCheckQuery : IRequest<bool>;
Tratamento de Exceções
Handler (pode suprimir a exceção)
public class NotFoundHandler : IRequestExceptionHandler<GetUserQuery, User, NotFoundException>
{
public Task Handle(GetUserQuery request, NotFoundException ex,
RequestExceptionHandlerState<User> state, CancellationToken ct)
{
state.SetHandled(User.Anonymous); // suprime a exceção e retorna alternativo
return Task.CompletedTask;
}
}
Action (side-effect, sempre relança)
public class ErrorLogger : IRequestExceptionAction<GetUserQuery, Exception>
{
public Task Execute(GetUserQuery request, Exception ex, CancellationToken ct)
{
logger.LogError(ex, "Erro ao processar {Request}", request);
return Task.CompletedTask; // exceção é repropagada automaticamente
}
}
Pre/Post Processors
public class ValidationPreProcessor<TRequest>(IValidator<TRequest> validator)
: IRequestPreProcessor<TRequest>
where TRequest : notnull
{
public async Task Process(TRequest request, CancellationToken ct)
{
var result = await validator.ValidateAsync(request, ct);
if (!result.IsValid) throw new ValidationException(result.Errors);
}
}
Contexto de Pipeline
public class AuditBehavior<TRequest, TResponse>(IPipelineContextAccessor accessor)
: IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
accessor.Context!.Set("userId", GetCurrentUserId());
return await next();
}
}
// No handler ou em outro behavior:
var userId = accessor.Context?.Get<string>("userId");
Dispatch Dinâmico
// Útil quando o tipo é descoberto em runtime (ex: mediator genérico)
object request = ResolveRequest(commandName);
object? result = await mediator.Send(request);
object notification = new SomeEvent();
await mediator.Publish(notification);
Estratégias de Publicação
cfg.DefaultPublishStrategy = PublishStrategy.ForeachAwait; // sequencial (padrão)
cfg.DefaultPublishStrategy = PublishStrategy.WhenAll; // paralelo, agrega exceções
cfg.DefaultPublishStrategy = PublishStrategy.WhenAllContinueOnError; // paralelo, ignora falhas
TypeEvaluator — Filtrar Assembly Scanning
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)
.TypeEvaluator = t => !t.Namespace?.StartsWith("MyApp.Legacy") ?? true;
Source Generator (registro zero-reflection)
O Proxos inclui um Source Generator que escaneia o assembly em compile-time e gera
um método AddProxosGenerated() com registro estático de todos os handlers.
Isso elimina completamente o assembly scanning em runtime — startup instantâneo.
O generator é automaticamente incluído ao instalar o pacote Proxos.
Analyzer PRX001
Quando um IRequest não tem handler correspondente, o analyzer emite um aviso em compile-time:
warning PRX001: 'CreateUserCommand' implementa 'IRequest<Unit>' mas não existe
'IRequestHandler<CreateUserCommand, Unit>' neste projeto.
OpenTelemetry
Proxos emite traces e métricas automaticamente. Configure o seu provider:
services.AddOpenTelemetry()
.WithTracing(b => b.AddSource(ProxosDiagnostics.ActivitySourceName))
.WithMetrics(b => b.AddMeter(ProxosDiagnostics.MeterName));
Métricas expostas:
proxos.requests.total— total de requests enviadosproxos.requests.failed— requests com falhaproxos.request.duration— duração em ms (histograma)proxos.notifications.total— notificações publicadasproxos.timeouts.total— requests cancelados por timeout
Proxos.Testing
var fake = new FakeMediator()
.Setup<GetUserQuery, User>(q => new User(q.Id, "Alice"))
.Returns<DeleteUserCommand, Unit>(Unit.Value);
// Verificações
Assert.True(fake.WasSent<GetUserQuery>());
Assert.True(fake.WasSent<GetUserQuery>(q => q.Id == 42));
Assert.Equal(1, fake.CountSent<GetUserQuery>());
Assert.True(fake.WasPublished<UserDeleted>());
Benchmarks
Medido com BenchmarkDotNet em .NET 9, Intel Core i7-1355U. O benchmark inclui criação de scope DI + resolução do mediator + dispatch — cenário realístico de ASP.NET Core.
Send()
| Método | Tempo | Alocações |
|---|---|---|
| MediatR 12.x | 300 ns | 544 B |
| Proxos | 382 ns | 568 B |
Publish()
| Método | Tempo | Alocações |
|---|---|---|
| MediatR 12.x | 338 ns | 448 B |
| Proxos | 588 ns | 440 B ✅ |
Medido com BenchmarkDotNet em .NET 9, Intel Core i7-1355U. Inclui criação de scope DI + resolução do mediator + dispatch — cenário realístico de ASP.NET Core.
Alocações: Proxos aloca apenas 4.4% a mais no Send e menos que o MediatR no Publish. O overhead de tempo (~1.3–1.7×) é irrelevante em produção — qualquer I/O (banco, HTTP) é 100–1000× mais lento que o dispatch. A principal vantagem é licença MIT, segurança em compile-time e funcionalidades que o MediatR não oferece.
Licença
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Proxos:
| Package | Downloads |
|---|---|
|
Proxos.Testing
Testing utilities for Proxos — FakeMediator with fluent setup and assertion API |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.2 | 105 | 3/25/2026 |