Autofac 3.0 will have a MultitenantIntegration support and its preview release is out now. To try it out, I created an ASP.NET Web API application with the following configuration:
public class Global : System.Web.HttpApplication {
protected void Application_Start(object sender, EventArgs e) {
var config = GlobalConfiguration.Configuration;
config.Routes.MapHttpRoute("Default", "api/{controller}");
RegisterDependencies(config);
}
public void RegisterDependencies(HttpConfiguration config) {
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// creates a logger instance per tenant
builder.RegisterType<LoggerService>().As<ILoggerService>().InstancePerTenant();
var mtc = new MultitenantContainer(
new RequestParameterTenantIdentificationStrategy("tenant"),
builder.Build());
config.DependencyResolver = new AutofacWebApiDependencyResolver(mtc);
}
}
It gets the job done and creates a LoggerService
instance as ILoggerService
per tenant. I have two problems at this stage which I wasn't able to solve:
RequestParameterTenantIdentificationStrategy
here as the TenantIdentificationStrategy just for this demo application. I am able to create my custom TenantIdentificationStrategy by implementing ITenantIdentificationStrategy
interface. However, TryIdentifyTenant
method of the ITenantIdentificationStrategy
makes you rely on a static instance such as HttpContext.Current
which is something that I don't want in an ASP.NET Web API environment as I want my API to be hosting agnostic (I know that I can delegate this work to the hosting layer but I would rather not to). Is there another way to achieve this in a way that I won't rely on a static instance?I also have a chance to register tenant specific instance as below:
mtc.ConfigureTenant("tenant1", cb => cb.RegisterType<Foo>()
.As<IFoo>().InstancePerApiRequest());
However, one of my situations requires me to pass the tenant name through the constructor parameter and I would love to have something like below:
mtc.ConfigureTenant((cb, tenantName) => cb.RegisterType<Foo>()
.As<IFoo>()
.WithParameter("tenantName", tenantName)
.InstancePerApiRequest());
Currently there is no such an API. Is there another way to achieve this or this kind of requirement doesn't make any sense?
Multitenant support has been available for a long time, it's just that 3.0 is the first time we've had a NuGet package for it. :)
The RequestParameterTenantIdentificationStrategy
is, as documented, just a very simple example showing one possible (and not recommended) way to identify tenant. You will have to choose for yourself how to identify your tenant based on the operating context. It could be from a web.config
value, an environment variable, or some other thing in the current environment. If you don't want to use HttpContext.Current
, don't. It's up to you to pick where you get that info from.
(A note on the RPTIStrategy
- the part that isn't recommended is using a querystring or request parameter as the tenant ID mechanism. I use HttpContext
in my production apps and it works fine. There's only so much you can abstract out before you have to actually touch the bare metal.)
There is no way out of the box to provide the lambda registration syntax you're asking for, primarily because tenant is not passed through the resolution process. The resolution process is:
It's intentionally simple and analogous to the existing operations. At the time of resolve, the sub-lifetime-scope belonging to the tenant is tagged with the tenant ID but the resolution operation doesn't know about the tenant ID... so the lambda wouldn't work (and probably won't anytime soon because it'd change the fundamental internals of the way Autofac works if it did).
To accomplish what you're looking for, you can use a combination of the InstancePerTenant
extension when registering...
var builder = new ContainerBuilder();
builder.RegisterType<Foo>().As<IFoo>().InstancePerTenant();
...and registering the ITenantIdentificationStrategy
as a dependency in your container.
builder.Register(myIdStrategy).As<ITenantIdentificationStrategy>();
Then make your class take an ITenantIdentificationStrategy
rather than the tenant ID directly. Use the strategy to get the tenant ID instead.
If you REALLY want to get fancy, you could register a keyed lambda that resolves the ID strategy, then gets the tenant ID. Then you could add a parameter registration to the object like you did but using a keyed service. (I'm going to go by memory now, so you'll have to double-check my syntax here, but it'll be something like this...)
builder.Register(c =>
{ var s = c.Resolve<ITenantIdentificationStrategy>();
object id;
s.TryIdentifyTenant(out id);
return id;
}).Keyed<object>("tenantId");
builder.RegisterType<Foo>()
.As<IFoo>()
.WithParameter(
(pi, c) => pi.Name == "tenantId",
(pi, c) => c.ResolveKeyed<object>("tenantId"))
.InstancePerApiRequest();
Again, you'll want to double-check me on that, but I'm pretty sure that (or a minor variation) should work to get you what you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With