I'm using asp.net mvc 4 to develop a multi-tenant mvc application.
I am using Autofac for the IOC container and have configured controllers to be registered for each client in different assemblies.
Autofac will switch out which controller it returns when asked to resolve depending on the current client context, which is determineid by looking at the route data.
I am getting an exception
Multiple types were found that match the controller named 'Home'.
Which would seem to indicate that Autofac is returning more than one match, however on closer inspection it seems that MVC is not even calling Autofac for controller resolution.
I can see a call that asks for an IControllerFactory from the DependencyResolver but never for the controller itself.
Do I need to implement my own controller factory ontop of using the dependency resolver?
I have had a look through the source code of mvc and from what I can tell the DefaultControllerFactory uses the dependencyresolver and should resolve the controllers but I'm having trouble debugging into it to see exactly whats happenning.
The tenant dlls are not referenced by the main website but copied in after build.
My global.asax looks as follows:
// First, create your application-level defaults using a standard
// ContainerBuilder, just as you are used to.
var builder = new ContainerBuilder();
var appContainer = builder.Build();
// Once you've built the application-level default container, you
// need to create a tenant identification strategy. The details of this
// are discussed later on.
var tenantIdentifier = new RouteDataTenantIdentificationStrategy();
// Now create the multitenant container using the application
// container and the tenant identification strategy.
var mtc = new MultitenantContainer(tenantIdentifier, appContainer);
// Configure the overrides for each tenant by passing in the tenant ID
// and a lambda that takes a ContainerBuilder.
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.AsQueryable()
.Where(a => a.IsDefined(typeof (PluginAssemblyAttribute), false));
foreach (var assembly in assemblies)
{
var pluginSpecification =
assembly.GetCustomAttributes(typeof(PluginAssemblyAttribute), false)
.OfType<PluginAssemblyAttribute>()
.Single();
var assembly1 = assembly;
mtc.ConfigureTenant(pluginSpecification.Tenant,
b => b.RegisterControllers(assembly1));
}
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc));
The tenant identification strategy looks as follows:
public class RouteDataTenantIdentificationStrategy
: ITenantIdentificationStrategy
{
public bool TryIdentifyTenant(out object tenantId)
{
tenantId = null;
var context = HttpContext.Current;
try
{
if (context == null || context.Request == null)
{
return false;
}
}
catch (HttpException)
{
// This will happen at application startup in MVC3
// integration since the ILifetimeScopeProvider tries
// to be resolved from the container at the point where
// a new AutofacDependencyResolver is created.
tenantId = null;
return false;
}
var routeData = RouteTable.Routes.GetRouteData(
new HttpContextWrapper(context));
if (routeData != null)
tenantId = routeData.Values.ContainsKey("tenant") ?
routeData.Values["tenant"] : null;
return tenantId != null;
}
}
EDIT: The full exception is
The request for 'Home' has found the following matching controllers:
MultiTenantViewEngine.Web.Controllers.HomeController
MultiTenantViewEngine.Web.Tenant1.Controllers.HomeController]
System.Web.Mvc.DefaultControllerFactory.GetControllerTypeWithinNamespaces(RouteBase route, String controllerName, HashSet`1 namespaces) +230
System.Web.Mvc.DefaultControllerFactory.GetControllerType(RequestContext requestContext, String controllerName) +833
System.Web.Mvc.DefaultControllerFactory.System.Web.Mvc.IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, String controllerName) +196
System.Web.Mvc.MvcRouteHandler.GetSessionStateBehavior(RequestContext requestContext) +267
System.Web.Mvc.MvcRouteHandler.GetHttpHandler(RequestContext requestContext) +61
System.Web.Mvc.MvcRouteHandler.System.Web.Routing.IRouteHandler.GetHttpHandler(RequestContext requestContext) +44
System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +352
System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e) +144
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +239
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +114
Looking at this in more depth it would seem to indicate that this error happens because GetControllerTypeWithinNamespaces() is returning more than one namespace.
Is there a way to overcome this?