rebus-org / Rebus.ServiceProvider

:bus: Microsoft Extensions Dependency Injection container adapter for Rebus
https://mookid.dk/category/rebus
Other
65 stars 32 forks source link

ServiceProviderProviderStep sets root provider instead of scoped provider on outgoing step #31

Closed vegar closed 4 years ago

vegar commented 4 years ago

When setting service provider on outgoing message pipeline, there is no check to see if there is a scoped service provider in the context already. Instead the root service provider is used.

This has implications when a message handler is sending outgoing messages. The scoped services are no longer available for the outgoing message. Isn't this a weakness? Or do I miss something here..?

vegar commented 4 years ago

Hum... I think maybe I'm confused - mixing up IServiceProvider and IServiceScope. I assumed the IServiceProvider you can get by doing a context.Load<IServiceProvider> would be a scoped provider. Now, after a fifth and sixth look, my understanding is that IServiceProvider will always be the root provider. The ServiceProviderProviderStep will never do anything with scopes. It will only provide you the root provider handed to it during configuration. This is true for both incoming and outgoing pipeline.

Scopes on the other hand is handled only for the incoming pipeline. The activator step will create a new scope and save it for further steps - or reuse a scope if already available in the context. If the message handler sends new messages, the outgoing pipeline should be able to reuse the same scope by loading it from the context, though. Right?

I guess this issue can be closed as 'no issue'...

mookid8000 commented 4 years ago

Yeah well.... you're correct about your observations:

  1. The service provider step just makes the root service provider available under IServiceProvider in the incoming step context:
var provider = context.Load<IServiceProvider();
  1. The oddly named handler activator, DependencyInjectionHandlerActivator, either a. Uses an existing ILifetimeScope from the incoming step context, or b. Creates a new ILifetimeScope (which it will then manage the lifetime of)

If the message handler sends new messages, the outgoing pipeline should be able to reuse the same scope by loading it from the context, though. Right?

Yes 🙂

I guess this issue can be closed as 'no issue'...

Happy to hear that 🤠

vegar commented 4 years ago

I'm not fully done, though.....

So - when sending a message from a message handler, I would need to fetch the IServiceScope from the incoming message context throught the transaction scope. That's doable.

But - when sending a message from a web request handler, I would like to set the IServiceScope from the http context. But as far as I can see, there is no way of getting hold of an active IServiceScope - unless you made it yourself.

Am I overthinking this?

Should I just add an outgoing step that

  1. either stores a IServiceScope on the context if it founds one on the incoming context,
  2. or overwrites the IServiceProvider with the one found on the HttpContext?

Any outgoing step handler needing a service provider would then need to check if there is a scope available and use that. If not, it needs to use the service provider, which might be scoped or not... ...not exactly as I would like it to be...

Do you have a better idea?

mookid8000 commented 4 years ago

(..) when sending a message from a web request handler, I would like to set the IServiceScope from the http context. But as far as I can see, there is no way of getting hold of an active IServiceScope - unless you made it yourself (..)

I think you can resolve IHttpContextAccessor and get the lifetime scope's service provider somewhat like this:

var context = accessor.HttpContext;
// this 👇 is the provider from the lifetime scope 
var provider = context.RequestServices;

Does that help you?