Lite.EventIPC is a cross-platform local Event Aggregator and remote IPC (inter-process communication) service library for C#. The pattern is used for decoupling publishers and subscribers in a single or multiple applications. The library can be easily extended for custom IPC transports using the IEventTransport interface to suit your needs (need I say, DBus?)
The Event Aggregator service in C# pattern is useful for decoupling publishers and subscribers in an application.
| Copyright 2025-2026 Xeno Innovations, Inc. (DBA: Suess Labs) |
| Created by: Damian Suess |
| Package | Stable | Preview |
|---|---|---|
| Lite.EventIpc |
This implementation is features:
- Simple, thread-safe, and async friendly
- Local subscribe/publish event aggregator
SubscribeandPublish- for one-way eventsSubscribeRequestandRequestAsync- for publishing with an awaited response (including timeouts)
- Uses weak references to avoid memory leaks
- Cleans up dead references during
Publish. - Prevents memory leaks when subscribers are no longer needed.
- DI-friendly with extensions and hosted service
- Built-in IPC transport mechanisms (inter-process communication) with JSON serialization:
- Named Pipe Transport
- Memory-Mapped File Transport (Windows OS only)
- TCP/IP Transport
- 2 types of IPC communication:
- One-way publish/subscribe (
IEventTransport) - (COMING SOON) Bidirectional request/response with timeouts (
IEventEnvelopeTransport)
- One-way publish/subscribe (
using Lite.EventIpc;
public class UserCreatedEvent
{
public string UserName { get; set; }
}
static void Main()
{
var eventAggregator = new EventAggregator();
// Subscribe
eventAggregator.Subscribe<UserCreatedEvent>(e =>
Console.WriteLine($"User created: {e.Username}"));
// Publish
eventAggregator.Publish(new UserCreatedEvent { Username = "Damian" })
}Subscribing to a request event allows users to respond with a different object. This is useful for RPC-like communication.
using Lite.EventIpc;
public async Task SubscribeWithResponse()
{
// Subscribe to Ping events that respond with Pong
_eventAggregator.SubscribeRequest<Ping, Pong>(EventSubscribe_PingAsync);
// Send a Ping request event and await the Pong response
Ping ping = new Ping("HELLO!");
Pong? response = await _eventAggregator.RequestAsync<Ping, Pong>(ping);
if (response is not null)
{
// Received PONG!
}
}
private async Task<Pong> EventSubscribe_PingAsync(Ping ping)
{
var response = "Received PING loud and clear";
return new Pong(response);
}Borrowing from the previous example, you can specify a timeout when sending a request event. If no subscriber responds within the timeout period, null is returned.
public async Task SubscribeWithResponse()
{
// ...
// Send a Ping request event and await the Pong response
Ping ping = new Ping("HELLO!");
var timeout = TimeSpan.FromMilliseconds(3000);
Pong? response = null;
try
{
response = await _eventAggregator.RequestAsync<Ping, Pong>(ping, timeout);
}
catch (TimeoutException)
{
// Handle timeout (no response received in time)
}
// ...
}If you store strong references to handlers, subscribers will never be collected. Using WeakReference ensures that if the subscriber is no longer needed, it can be GC'd.
- Option to include
Microsoft.Extensions.Loggerfor deep-logging
- Send IPC only for specified event types.
- Possibly, filtering or priority-based dispatching.
- Refactor
IEventTransporttoIIpcEventfor clarity. - Refactor
Transporternamespace toIpcorIpcTransportfor clarity. - Refactor
IEventEnvelopeTransportunderIpcReceiptednamespace for clarity. - Security: Named Pipes, MMF, and TCP should use proper ACLs / TLS / auth in production
