I'm going to assume that the DomainEvents static class has access to a statically configured container as mentioned by Udi's post.
With NServiceBus v6 we moved away from ambient context data such as thread locals and per thread scope because in the async world these concepts can be dangerous and error-prone. Instead, we encourage our users to float side-effect producing dependencies into things that need it over method injection or even better return the side effects and make it a top-level concern to decide whether the side effects should be written to the bus, database etc.
We understand this opinionated approach that we take is not always feasible to follow for projects with a history. One way to overcome the problem you are facing could be to float the dependency but rely on the container. Here are a few snippets that outline the refactoring:
public class MyDependencyUsedInVariousContexts
{
IBus bus;
public MyDependencyUsedInVariousContexts(IBus bus)
{
this.bus = bus;
}
// might be called from webapi or from within the handler
public void Do()
{
foreach (var changedCustomer in LoadChangedCustomers())
{
bus.Publish(new CustomerChanged { Name = changedCustomer.Name });
}
}
static IEnumerable<Customer> LoadChangedCustomers()
{
return Enumerable.Empty<Customer>();
}
}
[Route("api/[controller]")]
public class WebController : Controller
{
MyDependencyUsedInVariousContexts dependency;
public WebController(MyDependencyUsedInVariousContexts dependency)
{
this.dependency = dependency;
}
[HttpPost]
public IActionResult Create()
{
dependency.Do();
return null;
}
}
public class HandlerWithDependencyUsedInVariousContexts :
IHandleMessagesFromPreviousVersions<MyMessage>
{
MyDependencyUsedInVariousContexts dependency;
public HandlerWithDependencyUsedInVariousContexts(MyDependencyUsedInVariousContexts dependency)
{
this.dependency = dependency;
}
public void Handle(MyMessage message)
{
dependency.Do();
}
}
public class ContextDecorator
{
IMessageSession messageSession;
IMessageHandlerContext messageHandlerContext;
public ContextDecorator(IEndpointInstance session)
{
messageSession = session;
}
public ContextDecorator(IMessageHandlerContext context)
{
messageHandlerContext = context;
}
public Task Publish(object message)
{
if (messageSession != null)
{
return messageSession.Publish(message);
}
if (messageHandlerContext != null)
{
return messageHandlerContext.Publish(message);
}
throw new InvalidOperationException("Decorator was not properly resolved.");
}
}
public class MyDependencyUsedInVariousContextsNew
{
ContextDecorator bus;
public MyDependencyUsedInVariousContextsNew(ContextDecorator bus)
{
this.bus = bus;
}
// might be called from webapi or from within the handler
public async Task Do()
{
foreach (var changedCustomer in LoadChangedCustomers())
{
await bus.Publish(new CustomerChanged { Name = changedCustomer.Name })
.ConfigureAwait(false);
}
}
static IEnumerable<Customer> LoadChangedCustomers()
{
return Enumerable.Empty<Customer>();
}
}
[Route("api/[controller]")]
public class WebControllerNew : Controller
{
MyDependencyUsedInVariousContextsNew dependency;
// binding resolves ctor with IEndpointInstance
public WebControllerNew(MyDependencyUsedInVariousContextsNew dependency)
{
this.dependency = dependency;
}
[HttpPost]
public async Task<IActionResult> Create()
{
await dependency.Do();
return null;
}
}
public class HandlerWithDependencyUsedInVariousContextsNew :
IHandleMessages<MyMessage>
{
ScopeOrBetterConcreteFactory scope;
public HandlerWithDependencyUsedInVariousContextsNew(ScopeOrBetterConcreteFactory scope)
{
this.scope = scope;
}
public async Task Handle(MyMessage message, IMessageHandlerContext context)
{
var dependency = scope.Resolve<MyDependencyUsedInVariousContextsNew>(new NamedParameter("context", context));
await dependency.Do();
}
}
Another option is to switch back to ambient context and use AsyncLocal. Although we don't recommend that.