0

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...

Steven
  • 166,672
  • 24
  • 332
  • 435
Steve Dunn
  • 21,044
  • 11
  • 62
  • 87
  • 1
    I would say your solution is solid. Another option is to change your IMediator implementation. – Steven May 03 '19 at 10:23
  • Thanks @Steven- the `IMediator` implementation is the implementation from the MediatR pacakge. Will continue with my approach now I've heard from the man himself (which essentially is just wrapping the MedaitR implementation) – Steve Dunn May 03 '19 at 11:55
  • @Steven - the Answer is yours if you'd like to post it as an Answer. – Steve Dunn May 03 '19 at 11:56

1 Answers1

1

You basically have three options:

  • Define your own abstraction (as you are currently doing)
  • Replace the default IMediator implementation with one that applies scoping
  • Decorate the default IMediator implementation with a class that applies scoping

All 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.

Steven
  • 166,672
  • 24
  • 332
  • 435