Registering a different implementation of a service dependant on httpcontext in multi-tenant application

Tags: #<Tag:0x00007f2356f5ddd8>

Multi tenant application using asp mvc .net core 5. I have a ‘Tenant’ type that gets resolved using a custom resolver that uses the httpcontext to get the user’s current tenant. Resolution of this type is required to get a DbContext object (and others down the tree).

I can’t use httpcontext in hangfire so I’d like to be able to resolve this type using a different resolver (pulling the tenant ID from something other than the httpcontext). First thought was to implement a custom activator where I could detect that ‘Tenant’ was request and provide a different implementation. I’ve tried this:

GlobalConfiguration.Configuration.UseActivator(new HangfireServiceActivator(serviceProvider));

as well as the variation on this page

I couldn’t get those to call the custom activator, a breakpoint in the activator was never hit.

So then I also saw there is a ‘UseActivator’ option in the ‘AddHangfireServer’ service registration, and I tried this:

services.AddHangfireServer(o => o.Activator = new HangfireActivator(services.BuildServiceProvider()));

With that I get a warning to not use ‘BuildServiceProvider’. I am unsure of another way to get a ServiceProvider in ConfigureServices.

But as a larger question, am I pursuing this correctly? It is only the one type, ‘Tenant’, that I need to change the implementation for in Hangfire.

Had anyone else needed to implement a custom resolver for a multi-tenant application for hangfire when you don’t have a HttpContext?

Thanks,
Brian

I haven’t used a custom activator before so I have no idea regarding that issue.

However, we have a similar case where we get a “IUserSession” that is set on HttpContext object when using web application. For the Hangfire case we use another implementation of the IUserSession that is a scoped instance of an implemenation that isn’t using HttpContext.

First we need to register the IUserSession in the DI-container with a factory method. Current example uses Castle Windsor. E.g.

            Component.For<ScopedUserSession>().LifeStyle.Scoped(),
            Component.For<IUserSession>().UsingFactoryMethod((kernel, context)
                => HttpContext.Current != null
                    ? kernel.Resolve<HttpContextUserSession>()
                    : (IUserSession)kernel.Resolve<ScopedUserSession>()),

During execution we create a scope and initilize the user session for that scope. In some psuedo-code it is:

        using (scopedLifestyle.BeginScope())
        {
            var user = GetUserFromContext();
            resolver.Get<ScopedUserSession>().User = user;

            await InvokeTask();
        }

Thanks for this, I think this could lead to me a solution.