@ -72,40 +72,45 @@ Publishing by name with `new { OrderId = ..., CustomerEmail = ... }` triggers th
Dynamic subscription lets you register event handlers at runtime, using a string event name.
Dynamic subscription lets you register event handlers at runtime, using a string event name.
The recommended approach is to use `IocEventHandlerFactory`, which is the same mechanism ABP uses internally for typed handlers. It creates a new DI scope for each event, resolves a fresh handler instance, calls `HandleEventAsync`, then disposes the scope — so the handler can use normal constructor injection without any manual scope management:
```csharp
```csharp
public override async Task OnApplicationInitializationAsync(
public override void ConfigureServices(ServiceConfigurationContext context)
@ -115,7 +120,7 @@ public class PartnerOrderHandler : IDistributedEventHandler<DynamicEventData>
- **`EventName`** — the string name that identifies the event
- **`EventName`** — the string name that identifies the event
- **`Data`** — the raw event data payload (the deserialized `object` from the broker)
- **`Data`** — the raw event data payload (the deserialized `object` from the broker)
> `Subscribe` returns an `IDisposable`. Call `Dispose()` to unsubscribe the handler at runtime.
> `Subscribe` returns an `IDisposable`. Call `Dispose()` to unsubscribe the handler at runtime. For application-lifetime subscriptions, prefer module initialization (`OnApplicationInitializationAsync`) over subscribing inside an application service.
## Mixed Typed and Dynamic Handlers
## Mixed Typed and Dynamic Handlers
@ -126,7 +131,8 @@ Typed and dynamic handlers coexist naturally. When both are registered for the s
// Dynamic handler — receives DynamicEventData for the same event
// Dynamic handler — receives DynamicEventData for the same event
eventBus.Subscribe("OrderPlaced", new AuditLogHandler());
eventBus.Subscribe("OrderPlaced",
new IocEventHandlerFactory(scopeFactory, typeof(AuditLogHandler)));
```
```
When `OrderPlacedEto` is published (by type or by name), both handlers fire. The typed handler receives a fully deserialized `OrderPlacedEto` object. The dynamic handler receives a `DynamicEventData` wrapping the raw payload.
When `OrderPlacedEto` is published (by type or by name), both handlers fire. The typed handler receives a fully deserialized `OrderPlacedEto` object. The dynamic handler receives a `DynamicEventData` wrapping the raw payload.
Use the `Subscribe` overload that accepts a string event name:
The recommended way to subscribe is to implement `IDistributedEventHandler<DynamicEventData>` and use `IocEventHandlerFactory`. This mirrors how ABP manages typed handlers — it creates a new DI scope per event, resolves a fresh handler instance, calls `HandleEventAsync`, then disposes the scope:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
`Subscribe` returns an `IDisposable`. Call `Dispose()` to unsubscribe at runtime.
For simple stateless handlers that do not need DI services, you can also use `SingleInstanceHandlerFactory` with an inline handler:
````csharp
````csharp
var subscription = distributedEventBus.Subscribe(
var subscription = distributedEventBus.Subscribe(
@ -765,10 +802,8 @@ var subscription = distributedEventBus.Subscribe(
new SingleInstanceHandlerFactory(
new SingleInstanceHandlerFactory(
new ActionEventHandler<DynamicEventData>(eventData =>
new ActionEventHandler<DynamicEventData>(eventData =>
{
{
// Access the event name and raw data
var name = eventData.EventName;
var name = eventData.EventName;
var data = eventData.Data;
var data = eventData.Data;
return Task.CompletedTask;
return Task.CompletedTask;
})));
})));
@ -776,13 +811,7 @@ var subscription = distributedEventBus.Subscribe(
subscription.Dispose();
subscription.Dispose();
````
````
You can also subscribe using a typed distributed event handler:
> Do not inject `IServiceProvider` directly into a `SingleInstanceHandlerFactory`-based handler. Since the same instance is reused for every event, resolving scoped services directly from the root container causes a captive dependency and may throw a scope validation exception in development. Use `IocEventHandlerFactory` instead.