Reputation: 21741
I have an ASP.NET Web API using MediatR and SimpleInjector.
They are registered like this:
_container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
_container.Options.DefaultLifestyle = Lifestyle.Scoped;
_container.Collection.Register(typeof(INotificationHandler<>), typesFound);
I can publish events from my Controllers
:
[HttpGet]
public ActionResult<...> Get()
{
_mediator.Publish(new SomeEvent());
}
That works perfectly!
A new requirement is to listen for updates from an external system (Tibco). When an update occurs, the notification comes in via a C# event from another thread. In the C# event handler, I want to use MediatR to Publish
a notification:
void callbackFromTibco(object listener, MessageReceivedEventArgs @args)
{
_mediatr.Publish(new SomeEvent();
}
It's at this point that SimpleInjector throws an exception:
SomeEventHandler is registered as 'Async Scoped' lifestyle, but the instance is requested outside the context of an active (Async Scoped) scope
.
That's because the call stack originates from another thread whereas in the Controller
, the Controller itself was Scoped by SimpleInjector and hence MediatR creates the handler under the same scope.
Is it possible to do something so that I can register event handlers to be used in both circumstances?
I've gotten around this by creating an IPublishEvents
interface and a PublishEvents
class, where the PublishEvents
class looks like:
public PublishEvents(Container container, IMediator mediator)
{
_container = container;
_mediator = mediator;
}
public Task Publish(object notification, CancellationToken cancellationToken = default)
{
using (AsyncScopedLifestyle.BeginScope(_container))
{
return _mediator.Publish(notification, cancellationToken);
}
}
Is the abstraction the right approach? It certainly meets the Don't Marry the Framework
mantra, but aside from that, I'd like to know if there's a better way...
Upvotes: 0
Views: 966
Reputation: 172606
You basically have three options:
IMediator
implementation with one that applies scopingIMediator
implementation with a class that applies scopingAll three options are equally good, although defining your own application abstractions should typically have your preference, as that is in accordance to the Dependency Inversion Principle.
Upvotes: 1