【问题标题】:asp.net mvc 4 dependency resolver for controllers用于控制器的 asp.net mvc 4 依赖解析器
【发布时间】:2013-02-02 10:57:24
【问题描述】:

我正在使用 asp.net mvc 4 开发一个多租户 mvc 应用程序。

我将 Autofac 用于 IOC 容器,并已将控制器配置为在不同程序集中为每个客户端注册。

Autofac 将根据当前客户端上下文切换它返回的控制器,具体取决于当前客户端上下文,通过查看路由数据来确定。

我遇到了异常

找到了多个与名为“Home”的控制器匹配的类型。

这似乎表明 Autofac 返回的匹配项不止一个,但仔细观察后,似乎 MVC 甚至没有调用 Autofac 进行控制器解析。

我可以看到一个呼叫要求来自 DependencyResolverIControllerFactory,但从来没有要求控制器本身。

我是否需要在使用依赖解析器的基础上实现自己的控制器工厂?

我查看了 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


【解决方案1】:

您必须在所有路由中指定命名空间:

routes.MapRoute(
"Default",                                              // Route name
    "{controller}/{action}/{id}",                           // URL with parameters
    new { controller = "Home", action = "Index", id = "" },  // Parameter defaults
    "Some.NameSpace.To.Controllers" // required
);

如果您想在租户 DLL 中使用主控制器:要么从主站点 HomeController 重定向到它,要么创建您的自定义路由(从 Route 继承)并在其中进行命名空间选择。

【讨论】:

  • 好吧,取决于应用程序的上下文,我要么想在主站点 dll 中使用控制器,要么在tenant1 dll 或另一个租户 dll 中使用控制器,据此判断,最好编写我自己的控制器只要求依赖解析器给我类型的工厂?
  • 我宁愿使用自定义路由,因为它是决定加载哪个控制器的路由。 ControllerFactory 就是这样:一个工厂构造它所订购的控制器。
  • 我可以动态覆盖命名空间来搜索自定义路由吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-16
  • 1970-01-01
  • 1970-01-01
  • 2012-09-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多