EventTransit 2.0.1
See the version list below for details.
dotnet add package EventTransit --version 2.0.1
NuGet\Install-Package EventTransit -Version 2.0.1
<PackageReference Include="EventTransit" Version="2.0.1" />
<PackageVersion Include="EventTransit" Version="2.0.1" />
<PackageReference Include="EventTransit" />
paket add EventTransit --version 2.0.1
#r "nuget: EventTransit, 2.0.1"
#:package EventTransit@2.0.1
#addin nuget:?package=EventTransit&version=2.0.1
#tool nuget:?package=EventTransit&version=2.0.1
EventTransit
EventTransit is a high-performance, production-ready .NET messaging library that provides reliable message delivery with support for multiple message brokers. Built with clean architecture principles, it offers transactional outbox/inbox patterns, delayed messages, and comprehensive monitoring capabilities.
๐ Features
Core Capabilities
- โ Multi-Broker Support - RabbitMQ (with Kafka and Azure Service Bus planned)
- โ Transactional Outbox Pattern - Guaranteed message delivery with database transactions
- โ Inbox Pattern - Automatic duplicate detection and idempotency
- โ Delayed Messages - Schedule messages for future delivery using TTL + DLX pattern
- โ Dead Letter Handling - Automatic retry with exponential backoff and dead letter queues
- โ Direct Publishing - Fire-and-forget publishing for high-throughput scenarios
- โ Multiple Exchange Types - Direct, Topic, Fanout, and Headers exchanges
- โ Built-in Dashboard - Real-time monitoring UI for outbox, inbox, and dead letters
- โ Entity Framework Integration - Seamless integration with EF Core
- โ Repository Pattern Support - Use with or without Entity Framework
Production-Ready
- ๐ Type-Safe - Strongly-typed message contracts
- ๐ Observable - Comprehensive logging and metrics
- ๐ Resilient - Automatic reconnection and error recovery
- โก High Performance - Optimized for throughput and low latency
- ๐งช Well Tested - Extensive test coverage
- ๐ Well Documented - Complete API documentation and examples
๐ฆ Installation
dotnet add package EventTransit
๐ฏ Quick Start
1. Define Your Message
public class OrderCreatedEvent : IEventWithId
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public string OrderId { get; set; }
public decimal Amount { get; set; }
public DateTime CreatedAt { get; set; }
}
2. Configure EventTransit
Option A: Using appsettings.json (Recommended)
{
"EventTransit": {
"Broker": {
"BrokerType": "RabbitMQ",
"ConnectionString": "amqp://guest:guest@localhost:5672",
"ClientProvidedName": "my-service"
},
"DefaultExchange": {
"Name": "my.events",
"Type": "topic",
"Durable": true
},
"Outbox": {
"Enabled": true,
"ProcessingIntervalSeconds": 1,
"MaxRetryAttempts": 3,
"DeadLetterAfterMaxRetries": true
},
"Inbox": {
"Enabled": true,
"MaxRetryAttempts": 3
}
}
}
// Program.cs
builder.Services.AddEventTransitWithEntityFramework<MyDbContext>(
configureOptions: cfg => builder.Configuration.GetSection("EventTransit").Bind(cfg)
);
// Add Dashboard (optional but recommended)
builder.Services.AddEventTransitDashboardWithEntityFramework<MyDbContext>(builder.Configuration);
Option B: Programmatic Configuration
builder.Services.AddEventTransitWithEntityFramework<MyDbContext>(cfg =>
{
cfg.Broker.BrokerType = BrokerType.RabbitMQ;
cfg.Broker.ConnectionString = "amqp://guest:guest@localhost:5672";
cfg.DefaultExchange.Name = "my.events";
cfg.DefaultExchange.Type = ExchangeType.Topic;
cfg.Outbox.Enabled = true;
cfg.Inbox.Enabled = true;
});
3. Publish Messages
Using Outbox Publisher (Transactional - Recommended)
public class OrderService
{
private readonly IOutboxPublisher _publisher;
private readonly MyDbContext _dbContext;
public OrderService(IOutboxPublisher publisher, MyDbContext dbContext)
{
_publisher = publisher;
_dbContext = dbContext;
}
public async Task CreateOrderAsync(CreateOrderRequest request)
{
// Start transaction
using var transaction = await _dbContext.Database.BeginTransactionAsync();
try
{
// Save order to database
var order = new Order { /* ... */ };
_dbContext.Orders.Add(order);
await _dbContext.SaveChangesAsync();
// Publish event (saved to outbox table in same transaction)
await _publisher.PublishAsync(new OrderCreatedEvent
{
OrderId = order.Id,
Amount = order.Amount,
CreatedAt = DateTime.UtcNow
});
// Commit transaction
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
Using Direct Publisher (Fire-and-Forget)
public class NotificationService
{
private readonly IDirectPublisher _publisher;
public NotificationService(IDirectPublisher publisher)
{
_publisher = publisher;
}
public async Task SendNotificationAsync(string userId, string message)
{
await _publisher.PublishAsync(new NotificationEvent
{
UserId = userId,
Message = message,
SentAt = DateTime.UtcNow
});
}
}
Publishing Delayed Messages
// Publish a message to be delivered after 5 minutes
await _publisher.PublishDelayedAsync(
new ReminderEvent { UserId = "123", Message = "Don't forget!" },
delay: TimeSpan.FromMinutes(5)
);
4. Consume Messages
[MessageBinding("my.events", "order-service-queue", RoutingKey = "order.created")]
public class OrderCreatedConsumer : ConsumerBase<OrderCreatedEvent>
{
private readonly ILogger<OrderCreatedConsumer> _logger;
private readonly IOrderProcessor _orderProcessor;
public OrderCreatedConsumer(
ILogger<OrderCreatedConsumer> logger,
IOrderProcessor orderProcessor) : base(logger)
{
_logger = logger;
_orderProcessor = orderProcessor;
}
protected override async Task HandleMessageAsync(OrderCreatedEvent message)
{
_logger.LogInformation("Processing order {OrderId}", message.OrderId);
await _orderProcessor.ProcessOrderAsync(message.OrderId);
_logger.LogInformation("Order {OrderId} processed successfully", message.OrderId);
}
}
Consumer with Inbox (Automatic Duplicate Detection)
[MessageBinding("my.events", "payment-service-queue", RoutingKey = "order.created")]
public class PaymentConsumer : InboxConsumerBase<OrderCreatedEvent>
{
private readonly IPaymentService _paymentService;
public PaymentConsumer(
ILogger<PaymentConsumer> logger,
IInboxService inboxService,
IPaymentService paymentService) : base(logger, inboxService)
{
_paymentService = paymentService;
}
protected override async Task HandleMessageAsync(OrderCreatedEvent message)
{
// This will only be called once per unique message ID
// Duplicates are automatically detected and rejected
await _paymentService.ProcessPaymentAsync(message.OrderId, message.Amount);
}
}
5. Access the Dashboard
Navigate to: http://localhost:5000/eventtransit
The dashboard provides:
- ๐ Overview - Total messages, pending, completed, dead letters
- ๐ค Outbox - View all outgoing messages with filtering and search
- ๐ฅ Inbox - View all incoming messages with duplicate tracking
- โ ๏ธ Dead Letters - View failed messages with retry/republish capabilities
- ๐ Message Details - Click any message to see full payload, headers, and history
๐ Documentation
When to Use Which Publisher?
| Feature | Outbox Publisher | Direct Publisher |
|---|---|---|
| Guaranteed Delivery | โ Yes | โ No |
| Transactional | โ Yes | โ No |
| Survives Broker Downtime | โ Yes | โ No |
| Performance | Good | Excellent |
| Use Case | Critical business events | High-throughput notifications |
| Examples | Orders, Payments, Inventory | Logs, Metrics, Analytics |
Recommendation: Use Outbox Publisher for critical business events where you cannot afford to lose messages. Use Direct Publisher for high-throughput scenarios where occasional message loss is acceptable.
Exchange Types
// Direct Exchange - Point-to-point messaging
[MessageBinding("orders.direct", "order-queue", RoutingKey = "order.created")]
// Topic Exchange - Pattern-based routing
[MessageBinding("events.topic", "payment-queue", RoutingKey = "order.*.created")]
// Fanout Exchange - Broadcast to all queues
[MessageBinding("notifications.fanout", "email-queue")]
// Headers Exchange - Route based on message headers
[MessageBinding("tasks.headers", "priority-queue", Headers = new { priority = "high" })]
Configuration Options
For complete configuration options, see EVENTTRANSIT-DOCUMENTATION.md
๐๏ธ Architecture
EventTransit follows clean architecture principles:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your Application โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Publishers (IOutboxPublisher, IDirectPublisher) โ
โ Consumers (ConsumerBase<T>, InboxConsumerBase<T>) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ EventTransit Core โ
โ - Message Routing โ
โ - Serialization โ
โ - Error Handling โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Outbox/Inbox Patterns โ
โ - Transactional Outbox โ
โ - Duplicate Detection โ
โ - Retry Logic โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Broker Abstraction โ
โ - RabbitMQ โ
โ - Kafka (Coming Soon) โ
โ - Azure Service Bus (Coming Soon) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ง Advanced Features
Custom Message Headers
await _publisher.PublishAsync(message, headers: new Dictionary<string, object>
{
{ "correlation-id", correlationId },
{ "user-id", userId },
{ "priority", 5 }
});
Batch Publishing
var messages = new List<OrderCreatedEvent> { /* ... */ };
await _publisher.PublishBatchAsync(messages);
Manual Retry from Dashboard
- Navigate to Dead Letters page
- Select failed messages
- Click "Retry" or "Republish"
- Messages are requeued automatically
๐ Troubleshooting
"HandleMessageAsync doesn't exist" Error
If you encounter this error after installing EventTransit:
# Clear NuGet cache
dotnet nuget locals all --clear
# Restore without cache
dotnet restore --no-cache
# Rebuild
dotnet build
This ensures you're using the latest version of the library.
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
๐ License
This project is licensed under the MIT License.
๐ Acknowledgments
Built with โค๏ธ by Robert Orosume Eru
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 was computed. 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. |
-
net9.0
- Microsoft.AspNetCore.Mvc.Core (>= 2.2.5)
- Microsoft.Data.SqlClient (>= 6.1.1)
- Microsoft.Data.Sqlite (>= 9.0.6)
- Microsoft.EntityFrameworkCore (>= 9.0.6)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.6)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.6)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.6)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.9)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 9.0.6)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.6)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.9)
- Microsoft.Extensions.Options (>= 9.0.9)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.6)
- MySqlConnector (>= 2.4.0)
- Npgsql (>= 9.0.3)
- Polly (>= 8.5.2)
- Polly.Extensions (>= 8.5.2)
- RabbitMQ.Client (>= 7.1.2)
- System.ComponentModel.Annotations (>= 5.0.0)
- System.Text.Json (>= 9.0.6)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v2.0.1: Added delayed messages support, dashboard improvements, and bug fixes.