1

I am running into an interesting scenario that I just haven't been able to get answers on. I would like to see if there are elegant solutions for this. Here are some use cases:

  1. I want to pass the identity/user principal of the user that called an API Action in a Controller to Services and Repository layers. There are important decisions that get taken at Service layer depending on the User's claims and/or privileges/permissions.

  2. I want to identify a tenant (occurs using HTTP Headers or Hostnames) but pass this Tenant information to Services and Repository layers so that different Redis Caches and Databases can be used depending on the Tenant.

I can easily build a method in a base service called WithTenant(object tenant); which I can use to pass tenants.

This means every service call I make will need this WithTenant called.

For e.g. a call:

 _inventoryService.CheckInventory() 

will now change to:

_inventoryService.WithTenant(tenant).CheckInventory();

Likewise, if I want to pass the HttpRequest in addition to the Tenant, I will have to do this:

_inventoryService.WithHttpRequest(Request).WithTenant(tenant).CheckInventory();

This gets even more complex as I need to pass this to the Repository layer. My Mongo DB Context is a singleton. I might need to change this to accept the tenant so the connection string can be changed based on the tenant.

So I was thinking:

  1. Isn't there a way by which I can pass a Context object across these layers? Maybe build a Context class and set properties like Request, or Tenant or anything else I need and then pass it across layers?
  2. More importantly, is there a way to dynamically inject this into the Constructors of these services so I don't need to manually wire it up again? I know that I can store my context in the Request object or maybe even an Owin Context. Could I somehow inject that? If so I haven't seen any good examples of this across multiple layers.

I guess I'm simply trying to simplify the ability to pass state between layers. Cross cutting functions like Caching, Logging, Database Access change on a per Tenant basis so if I could create a central context, that would greatly simplify things.

Any thoughts? Would love to see what others are doing to solve such problems.

  • Couldn't you make the tenant part of your security context? – Robert Harvey Sep 19 '17 at 19:27
  • Hello, this is a REST based API that also allows anonymous access. I could potentially assign a client_id and tie that back in with the tenant and use that within the OAuth Token as a Claim to identify the tenant. I would still need to query for the connection strings etc. in each layer as I wouldn't want to pass them as claims. This approach might work with a few quirks. curious to see if there are other ways in which this can be done. – Anup Marwadi Sep 19 '17 at 22:03
2

to everyone that may want to know how this was solved, it turned out to be an easy fix with a little bit of understanding of Dependency Injection Lifetimes as well as clean Architecture strategies.. The following was the logic I applied:

  1. Identify the Tenant - Look up the Tenant based on the available strategies (i.e. either a Tenant Id in a Claim, or a Host Header, or the originating request).

  2. Get the Tenant's Profile - Once the Tenant was identified, his profile information was obtained. This included Tenant's Data center information and various other parameters (queues, caching cluster etc)

  3. Build a Tenant Context (instantiate singletons for Redis, Mongo and maintain a Connection string for SQL Databases). Keep this Tenant Context in a Memory Cache so future retrieval is easy.

  4. Built a Per Resolve Tenant Context resolution using Unity (or a similar DI tool). Per Resolve ensures that the Tenant Context resolution only occurs once during a request and the same instance can be used across the board. This was important because the same Tenant Context was needed at Attribute Filter level, Application and Domain Services and sometimes all the way up to the Repository layer. All these would have the same context instead of building a new one for each class up/down the food-chain.

  5. Use the context at each layer to get a connection factory to connect to Mongo, Redis, SQL or Queues.

Once I had the factory, a global replace allowed me to use it across the board (got lucky because I was already using a cleaner architecture).

If anyone is really interested in peeking into the code/details, here is a link that might help: http://www.hypertrends.com/2017/10/saaskit-dependency-injection-tenants/

1

It seems to me that User is an input to CheckInventory and should be a parameter.

While tenant on the other hand tells you what service to call the CheckInventory Method on.

I would have something like

public class myController
{
    private Dictionary<string,IService> services;

    public void CheckInv()
    {
         var tennant //from header
         var user //from token
         services[tenant.Id].CheckInv(user);
    }
}

You can inject your list of services into the controller and the correct mongoDb repository (dont use static) into each service.

Alternatively wrap the dictionary in a factory class so you can deal with tenant not found, caching and other edge cases.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.