Search code examples
.net-coredependency-injectioncontainersautofacmulti-tenant

Autofac multitenant - override tenant in runtime


I have a .net core web application that uses Autofac multitenancy container. The tenant strategy resolves the tenant by looking at the path of the HTTP requests.

However, there is a specific functionality in which a tenant A needs to use configuration of another tenant B (a sub-tenant in this case); the problem is that it is not known until tenant A has already performed some logic to know which sub-tenant's configuration it needs to use.

Is there a way to obtain the service of another tenant in runtime?

I will try to clarify with an example:

What I have is more or less:

  • An HTTP request to GET my.host.net/A/rules
  • The tenant resolver is capable of identifying that current tenant is A (it is in the path, just after the host name)
  • The tenant resolver gets from the database the general rules, and one of them indicates that the configurations of another tenant B should be used
  • From here on, I would like to use the services of tenant B.

What I have tried / think about?

  • Save the Multitenant container and use GetTenantScope to resolve the scope of tenant B in a class factory that resolves the services to use. However, I don't know the implications in terms of memory usage and possible problems with mixing tenants
  • Forget about multitenancy and just save configurations per tenant in specific class.

Solution

  • I'm not sure what "sub-tenant" means in this context. Autofac has no notion of multi-level tenancy in its multitenant support, so while this construct may make sense in the context of your application, trying to make that work in Autofac is not going to be simple.

    Trying to switch tenants mid-request is going to be a challenge at best. Things through the request pipeline (middleware, controllers, etc.) are all going to want to use the HttpContext.RequestServices which is set first thing in the request. Like, it's literally the very first middleware that runs. Once it's set, the pipeline starts resolving and controllers and other things start resolving and... it's locked into that tenant. You can't switch it.

    Given that, I'd caution you about trying to resolve some things from one tenant, switching mid-request, and resolving the rest from a different tenant. It's likely you'll get inconsistencies.

    Say you have a middleware instance that takes an ISomeCoolService. You also have a controller that needs ISomeCoolService, but you're using the special tenant-switching logic in the controller instead of taking it as a dependency. During the middleware execution, the middleware will get tenant A's ISomeCoolService but the controller will use tenant B's ISomeCoolService and now you've got application behavior inconsistency. Trying to ensure consistency with the tenant switching is going to be really, really hard.

    Here's what I'd recommend:

    • If you can do all the tenant determination up front in the ITenantIdentificationStrategy and cache that in, say, HttpContext.Items so you don't have to look it up again - do that. The very first hit in the pipeline might be slow with the initial tenant determination logic but after that the ITenantIdentificationStrategy can look in HttpContext.Items for the tenant ID instead of doing the database call and it'll be fast. This will stop you from having to switch tenants mid-request.
    • If you can't do the tenant determination up front and you need the pipeline to execute a while before you can figure it out... you may need a different way to determine the tenant. Truly, try to avoid switching tenants. It will cause you subtle problems forever.
    • Don't try to get "tenant inheritance" working, at least not with the stock Autofac Multitenant support. I recognize that it would be nice to say "some services are tenant A but others are tenant B and it inherits down the stack" but that's not something built into the multitenant support and is going to be really hard to try to force to work.

    If you really, really, really are just dedicated to getting this tenant "hierarchy" thing working, you could try forking the Autofac.Multitenant support and implementing a new MultitenantContainer that allows for sub-tenants. The logic of the MultitenantContainer isn't actually that complex, it's just storing a tagged lifetime scope per tenant. Hypothetically, you could add some functionality to enable sub-tenant configuration. It won't be five minutes of work, and it's not really something we'd plan on adding to Autofac, so it would be a total fork that you get to own, but you could possibly do it.