CodeWF.EventBus
CodeWF.EventBus is a lightweight in-process event bus library suitable for module decoupling in WPF, WinForms, Avalonia UI, ASP.NET Core, and console applications. It supports Command command dispatching and Query<T> query callbacks, making it easy to implement simple CQRS.
Suitable Scenarios
- Desktop application modules do not want to directly reference each other's services or ViewModels.
- In ASP.NET Core, you want to use a lightweight event bus to decouple controllers, business handlers, and query logic.
- Console, WinForms, WPF, or Avalonia projects lack a unified IOC container but still want to decouple processes using publish/subscribe.
- You want a solution smaller than
MediatRwith fewer framework constraints, while retaining the Command/Query pattern.
Package Selection
| Package | Use Case |
|---|---|
CodeWF.EventBus |
No IOC container or manual subscription. |
CodeWF.AspNetCore.EventBus |
ASP.NET Core / MS.DI. |
CodeWF.DryIoc.EventBus |
DryIoc / Prism. |
CodeWF.IOC.EventBus |
Other IOC containers, with callback-based registration and resolution. |
Core Types
public abstract class Command
{
}
public abstract class Query<TResponse> : Command
{
public abstract TResponse Result { get; set; }
}
Example:
public sealed class CreateProductCommand : Command
{
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
}
public sealed class ProductQuery : Query<ProductItemDto?>
{
public Guid ProductId { get; set; }
public override ProductItemDto? Result { get; set; }
}
Handler Writing
Event handler methods are marked with [EventHandler], can have only one parameter, which must inherit from Command. Return types support void and Task, and methods can be public, private, or static.
[Event]
public sealed class ProductEventHandler
{
[EventHandler]
private async Task HandleCreateAsync(CreateProductCommand command)
{
await Task.CompletedTask;
}
[EventHandler(Order = 1)]
private void HandleQuery(ProductQuery query)
{
query.Result = new ProductItemDto
{
Id = query.ProductId,
Name = "Demo",
Price = 99
};
}
}
Without IOC Container
public sealed class MainViewModel
{
private readonly IEventBus _eventBus = EventBus.Default;
public MainViewModel()
{
_eventBus.Subscribe(this);
}
[EventHandler]
private void Handle(UpdateTimeCommand command)
{
Console.WriteLine(command.Time);
}
}
Publishing commands and queries:
await EventBus.Default.PublishAsync(new CreateProductCommand
{
Name = "XiaoMi",
Price = 8999
});
var product = await EventBus.Default.QueryAsync(new ProductQuery
{
ProductId = Guid.NewGuid()
});
When an instance object is no longer needed, it is recommended to actively unsubscribe:
EventBus.Default.Unsubscribe(this);
ASP.NET Core
using CodeWF.AspNetCore.EventBus;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEventBus();
var app = builder.Build();
app.MapControllers();
app.UseEventBus();
app.Run();
AddEventBus() scans the assembly for [Event] classes and registers them in the container; UseEventBus() connects the scanned handlers to the event bus.