Interlink 1.5.2
Prefix Reserveddotnet add package Interlink --version 1.5.2
NuGet\Install-Package Interlink -Version 1.5.2
<PackageReference Include="Interlink" Version="1.5.2" />
<PackageVersion Include="Interlink" Version="1.5.2" />
<PackageReference Include="Interlink" />
paket add Interlink --version 1.5.2
#r "nuget: Interlink, 1.5.2"
#:package Interlink@1.5.2
#addin nuget:?package=Interlink&version=1.5.2
#tool nuget:?package=Interlink&version=1.5.2
Interlink
Interlink is a lightweight and modern mediator library for .NET, designed to decouple your code through request/response and notification patterns. Built with simplicity and performance in mind, it helps streamline communication between components while maintaining a clean architecture.
โจ Features
- ๐งฉ Simple mediator pattern for request/response
- ๐ Publish/Subscribe notification system
- ๐ฏ Unified
IMediator(combinesISender+IPublisher) - โช
Unitsupport for fire-and-forget / void commands (IRequest/IRequest<Unit>) - ๐ง Pipeline behaviors (logging, validation, etc.)
- ๐ง Clean separation of concerns via handlers
- ๐ช Dependency injection support out of the box
- ๐ Pre and Post Processors for enhanced lifecycle control
- ๐ Assembly scanning for automatic handler registration
- ๐งช Custom service factory injection
- ๐ Pipeline ordering via attributes or configuration
- ๐จ Dedicated
HandlerNotFoundException - โ Compatible with .NET Standard 2.0+ to .NET 10
- ๐ฆ Optional packages: Logging, FluentValidation, ASP.NET Core, Analyzer
๐ฆ Installation
dotnet add package Interlink
Optional packages:
dotnet add package Interlink.Extensions.Logging
dotnet add package Interlink.Extensions.Validation
dotnet add package Interlink.AspNetCore
dotnet add package Interlink.Analyzers
โ๏ธ Setup
Register Interlink in Program.cs (or Startup.cs):
builder.Services.AddInterlink();
Scan a specific assembly:
builder.Services.AddInterlink(typeof(MyHandler).Assembly);
Configure pipeline behaviors and optional custom factory:
builder.Services.AddInterlink(options =>
{
// Open-generic behaviors (order is optional; lower runs first / outermost)
options.AddBehavior(typeof(LoggingBehavior<,>), order: 0);
options.AddBehavior(typeof(ValidationBehavior<,>), order: 1);
// Optional custom resolution factory
options.ServiceFactory = type => /* your custom resolver */;
}, typeof(MyHandler).Assembly);
With the extension packages:
builder.Services.AddInterlink(typeof(MyHandler).Assembly);
builder.Services.AddInterlinkLogging();
builder.Services.AddInterlinkValidation(typeof(MyValidator).Assembly);
builder.Services.AddInterlinkAspNetCore(); // registers exception filter
๐จ Request / Response Pattern
1. Define a request and handler
With response
using Interlink;
using Interlink.Contracts;
public class GetAllPets
{
public sealed record Query : IRequest<List<string>>;
public sealed class Handler : IRequestHandler<Query, List<string>>
{
public Task<List<string>> Handle(Query request, CancellationToken cancellationToken)
{
var pets = new List<string> { "Dog", "Cat", "Fish" };
return Task.FromResult(pets);
}
}
}
Without response (using Unit)
using Interlink;
using Interlink.Contracts;
public class CreatePet
{
public sealed record Command(string Name) : IRequest; // or IRequest<Unit>
public sealed class Handler : IRequestHandler<Command, Unit>
{
public Task<Unit> Handle(Command request, CancellationToken cancellationToken)
{
// Save pet to database...
Console.WriteLine($"Pet '{request.Name}' created");
return Unit.Value; // or Task.FromResult(Unit.Value)
}
}
}
Note
IRequestis equivalent toIRequest<Unit>.- Always return
Unit.Value(orTask.FromResult(Unit.Value)) from handlers that produce no meaningful response.
2. Send the request
[ApiController]
[Route("api/[controller]")]
public class PetController(IMediator mediator) : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetAllPets(CancellationToken cancellationToken)
{
var pets = await mediator.Send(new GetAllPets.Query(), cancellationToken);
return Ok(pets);
}
[HttpPost]
public async Task<IActionResult> CreatePet(string name, CancellationToken cancellationToken)
{
await mediator.Send(new CreatePet.Command(name), cancellationToken);
return NoContent();
}
}
You can also inject ISender if you only need request/response functionality.
If no handler is registered, Send throws HandlerNotFoundException.
๐ฃ Notifications (Publish / Subscribe)
1. Define a notification
public sealed class UserCreated(string userName) : INotification
{
public string UserName { get; } = userName;
}
2. Create one or more handlers
public sealed class SendWelcomeEmail : INotificationHandler<UserCreated>
{
public Task Handle(UserCreated notification, CancellationToken cancellationToken)
{
Console.WriteLine($"Welcome email sent to {notification.UserName}");
return Task.CompletedTask;
}
}
public sealed class WriteAuditLog : INotificationHandler<UserCreated>
{
public Task Handle(UserCreated notification, CancellationToken cancellationToken)
{
Console.WriteLine($"Audit: user {notification.UserName} created");
return Task.CompletedTask;
}
}
3. Publish
public class AccountService(IMediator mediator)
{
public async Task RegisterUser(string username)
{
// Save to DB...
await mediator.Publish(new UserCreated(username));
}
}
You can also inject IPublisher if you only need notification publishing.
๐งฌ Pipeline Behaviors
Pipeline behaviors wrap the handler and can run logic before and after it.
Signature (correct order)
public interface IPipelineBehavior<in TRequest, TResponse>
where TRequest : notnull
{
Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken);
}
Example behavior
public sealed class TimingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
var sw = Stopwatch.StartNew();
var response = await next(cancellationToken);
sw.Stop();
Console.WriteLine($"{typeof(TRequest).Name} took {sw.ElapsedMilliseconds} ms");
return response;
}
}
Ordering
Use the attribute (lower value runs first / outermost):
[PipelineOrder(1)]
public sealed class FirstBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
Console.WriteLine("First behavior");
return await next(cancellationToken);
}
}
[PipelineOrder(2)]
public sealed class SecondBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
Console.WriteLine("Second behavior");
return await next(cancellationToken);
}
}
Or supply the order when registering:
builder.Services.AddInterlink(options =>
{
options.AddBehavior(typeof(FirstBehavior<,>), order: 1);
options.AddBehavior(typeof(SecondBehavior<,>), order: 2);
});
๐ Pre and Post Processors
Pre-processors run before the pipeline.
Post-processors run after a successful pipeline.
public sealed class MyRequestPreProcessor : IRequestPreProcessor<GetAllPets.Query>
{
public Task Process(GetAllPets.Query request, CancellationToken cancellationToken)
{
Console.WriteLine("[Pre] GetAllPets");
return Task.CompletedTask;
}
}
public sealed class MyRequestPostProcessor : IRequestPostProcessor<GetAllPets.Query, List<string>>
{
public Task Process(GetAllPets.Query request, List<string> response, CancellationToken cancellationToken)
{
Console.WriteLine($"[Post] returned {response.Count} pets");
return Task.CompletedTask;
}
}
They are discovered automatically by AddInterlink().
๐ Built-in Logging Behavior
dotnet add package Interlink.Extensions.Logging
builder.Services.AddInterlinkLogging();
This registers LoggingBehavior<TRequest, TResponse>, which logs:
- request start
- successful completion + elapsed milliseconds
- exceptions
โ FluentValidation Integration
dotnet add package Interlink.Extensions.Validation
// Registers ValidationBehavior + scans for IValidator<T>
builder.Services.AddInterlinkValidation(typeof(CreateUserValidator).Assembly);
Example validator:
public sealed class CreateUserValidator : AbstractValidator<CreateUser.Command>
{
public CreateUserValidator()
{
RuleFor(x => x.Email).NotEmpty().EmailAddress();
RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
}
}
When validation fails, a FluentValidation.ValidationException is thrown (mapped to 400 by the ASP.NET Core filter if you use it).
๐ ASP.NET Core Integration
dotnet add package Interlink.AspNetCore
builder.Services.AddControllers();
builder.Services.AddInterlinkAspNetCore(); // adds InterlinkExceptionFilter
The filter maps:
| Exception | HTTP Status | Response |
|---|---|---|
HandlerNotFoundException |
404 | ProblemDetails |
ValidationException* |
400 | ValidationProblemDetails |
* FluentValidation support is optional and detected at runtime (no hard dependency).
๐ Analyzer (missing handler detection)
dotnet add package Interlink.Analyzers
Produces diagnostic ILINK001 (warning) when a type implements IRequest<TResponse> but no corresponding IRequestHandler<TRequest, TResponse> is found in the compilation.
๐ฆ API Overview
Core contracts
public interface IRequest<out TResponse> { }
// Non-generic form (equivalent to IRequest<Unit>)
public interface IRequest : IRequest<Unit> { }
public interface IRequestHandler<in TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}
public interface INotification { }
public interface INotificationHandler<in TNotification>
where TNotification : INotification
{
Task Handle(TNotification notification, CancellationToken cancellationToken);
}
Unit
/// <summary>
/// Represents a void response. Use this when a request does not return a meaningful value.
/// </summary>
public readonly struct Unit : IEquatable<Unit>
{
public static readonly Unit Value = default;
public static Task<Unit> Task => System.Threading.Tasks.Task.FromResult(Value);
...
}
- Prefer
IRequest(orIRequest<Unit>) for commands that only perform an action. - Always return
Unit.Value(orTask.FromResult(Unit.Value)) from the corresponding handler.
Sender, Publisher & Mediator
public interface ISender
{
Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);
Task Send(IRequest request, CancellationToken cancellationToken = default); // convenience for Unit
}
public interface IPublisher
{
Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : INotification;
}
/// <summary>
/// Unified mediator that combines request/response and notification publishing.
/// </summary>
public interface IMediator : ISender, IPublisher
{
}
Pipeline
public delegate Task<TResponse> RequestHandlerDelegate<TResponse>(CancellationToken cancellationToken = default);
public interface IPipelineBehavior<in TRequest, TResponse>
where TRequest : notnull
{
Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken);
}
Pre / Post processors
public interface IRequestPreProcessor<in TRequest> where TRequest : notnull
{
Task Process(TRequest request, CancellationToken cancellationToken);
}
public interface IRequestPostProcessor<in TRequest, in TResponse> where TRequest : notnull
{
Task Process(TRequest request, TResponse response, CancellationToken cancellationToken);
}
Exception
public class HandlerNotFoundException : InvalidOperationException
{
public Type RequestType { get; }
public Type? HandlerType { get; }
}
๐ Roadmap status
| Version | Status | Highlights |
|---|---|---|
| 1.0 โ 1.3 | โ Released | Core mediator, notifications, pipelines, pre/post, performance |
| 1.4 | โ Released | .NET Standard 2.0+ |
| 1.5 | โ Released | Logging, Validation, ASP.NET Core, Analyzer, exceptions, ordering fixes |
| 1.5.1 | โ Released | Unit support for fire-and-forget / void commands (IRequest / IRequest<Unit>) |
| 1.5.2 | โ Current | Added unified IMediator interface (composes ISender + IPublisher) |
Future ideas
- Request cancellation / timeout behaviors
- Metrics & tracing support
- Dynamic / externalized pipeline configuration
๐ License
MIT License ยฉ ManuHub
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.CSharp (>= 4.7.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.11)
-
net10.0
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Interlink:
| Package | Downloads |
|---|---|
|
Interlink.AspNetCore
ASP.NET Core integration for the Interlink mediator library (filters, exception handling, helpers). |
|
|
Interlink.Extensions.Validation
FluentValidation integration for the Interlink mediator library. |
|
|
Interlink.Extensions.Logging
Built-in logging pipeline behavior for the Interlink mediator library. |
GitHub repositories
This package is not used by any popular GitHub repositories.