Tenant Injection in multi-tenancy scenario

May 2, 2014 at 7:38 AM
Hi,

I am a newbie to using DI and Simple Injector.

I am working on a multi-tenant MVC Web API application.

I am looking for a way to inject the current tenant instance into all business logic providers. I have a way to identify the tenant and create corresponding entity instance, but I don't know how to provide this instance of Simple Injector, so that it can inject it into the business logic providers when it creates an instance of the business logic providers.

Thanks,
Vikram Lele
Coordinator
May 2, 2014 at 7:51 AM
It seems you are trying to inject an entity (your Tenant entity) into services. Don't do that. Objects such as messages DTOs and entities are short lived, runtime values, but dependency injection is a technique for building up object graphs of services, who tend to be long lived and stateless. They should be separated clearly.

In your case it seems logical to inject some sort of ITenantContext service that allows access to the current Tenant entity or information:
public interface ITenantContext
{
    Tenant CurrentTenant { get; }
}
Here is some related information:
May 2, 2014 at 8:23 AM
Edited May 5, 2014 at 6:08 AM
Hi DNJ,

Thanks for your inputs.

Each of our tenant uses a different access URL, all access URLs point to the same deployment folder (don't know if this is a good idea or bad, but that aside). So, essentially, we identify the tenant based on the access URL used.

We have a custom action filter, in that we identifies the tenant and, at this point we have the Tenant instance available. Unfortunately, by this time, the controller is already created (which also means that business logic provider is already injected).

If I follow what you are suggesting, I should also inject ITenantContext into the controller or business logic provider. This looks like a decent approach, though, I know need to figure out how TenantContextImpl will identify the tenant. That, I guess, I should be able to do using HttpContext.Current.

Thanks,

Vikram Lele
Coordinator
May 2, 2014 at 2:29 PM
Hi Vikram,

You should inject the ITenantContext into every service that directly needs to make decisions based on the tenant. Since you base the tenant on the URL, I think the HttpContext.Current.Request would be a good place to start. The solution shouldn't be that much different from what you're already doing in the custom action filter; that shouldn't be rocket science.

Good luck.
May 3, 2014 at 7:22 AM
Edited May 3, 2014 at 7:22 AM
Hi DNJ,

Thanks. That worked like a charm.

Just one thing for anyone else trying this - don't access HttpContext.Current in the constructor of ITenantContext implementation, that will fail the container.Verify() call.

Vikram
Coordinator
May 3, 2014 at 7:33 AM
Remember how you should separate buildng the object graph from the creation of runtime values. HttpContext is a runtime value. Accessing it from a service's contructor couples them again.
May 5, 2014 at 5:10 AM
Edited May 5, 2014 at 6:08 AM
Hi DNJ,

Does that mean that I cannot have a member caching the value in the implementation as in the code below?
public class TenantContextImpl : ITenantContext 
{
    private Tenant _tenant = null;

    public Tenant CurrentTenant 
    {
        get
        {
           if (_tenant == null)
           {
             // GetCurrentTenant gets the current tenant from repository
             _tenant = GetCurrentTenant();
           }

            return _tenant;
        }
    }
}
If TenantContextImpl instance is getting cached, I would be in trouble here.

Vikram
Coordinator
May 5, 2014 at 10:21 AM
This won't get you in trouble. As a matter of fact, this would be a quite natural thing to do. This ensures that the Tenant isn't loaded during object graph construction, while you benifit from the performance improvement that caching gets you. The only thing to make sure is that the TenantContextImpl is configured to have a lifestyle of per-request or shorter, since you will obviously get in trouble if you register it as singleton.

As an alternative implementation, you can also make use of .NET's Lazy<T> class to do the initialization (but the effect is exactly the same):
public class TenantContextImpl : ITenantContext
{
    private readonly Lazy<Tenant> _tenant = new Lazy<Tenant>(GetCurrentTenant);

    public Tenant CurrentTenant
    {
        get { return _tenant.Value; }
    }

    private static Tenant GetCurrentTenant() { }
}