【发布时间】:2013-02-02 10:57:24
【问题描述】:
我正在使用 asp.net mvc 4 开发一个多租户 mvc 应用程序。
我将 Autofac 用于 IOC 容器,并已将控制器配置为在不同程序集中为每个客户端注册。
Autofac 将根据当前客户端上下文切换它返回的控制器,具体取决于当前客户端上下文,通过查看路由数据来确定。
我遇到了异常
找到了多个与名为“Home”的控制器匹配的类型。
这似乎表明 Autofac 返回的匹配项不止一个,但仔细观察后,似乎 MVC 甚至没有调用 Autofac 进行控制器解析。
我可以看到一个呼叫要求来自 DependencyResolver 的 IControllerFactory,但从来没有要求控制器本身。
我是否需要在使用依赖解析器的基础上实现自己的控制器工厂?
我查看了 mvc 的源代码,据我所知,DefaultControllerFactory 使用了依赖解析器并且应该解析控制器,但我在调试它以查看到底发生了什么时遇到了麻烦。
租户 dll 不被主网站引用,而是在构建后复制。
我的global.asax 如下所示:
// 首先,使用标准创建应用程序级别的默认值
// ContainerBuilder,就像你习惯的那样。
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));
租户识别策略如下:
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;
}
}
编辑:完整的例外是
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
更深入地查看这一点似乎表明发生此错误是因为 GetControllerTypeWithinNamespaces() 返回多个命名空间。
有没有办法克服这个问题?
【问题讨论】:
-
你的 RouteDataTenantIdentificationStrategy 是什么样子的,你有没有在里面放一个断点,看看它是否在控制器被解析时被调用?
-
刚刚添加它,将再次检查它是否被调用
-
它似乎正在被调用并返回正确的租户 ID
-
如果你还没有,你应该看看 Zack Owens 的文章。 weblogs.asp.net/zowens/archive/2010/05/26/…
-
是的,我看到这可能是我最终要走的路线
标签: asp.net-mvc-4 controller inversion-of-control autofac service-locator