0

_ __ _ __ _ ____更新__ _ __ _ __ _ ___

从不同的角度使用自定义 ControllerFactory 解决问题,请参见下面的答案。

_ __ _ ____原始问题__ _ __ _ __

我有一个用于多个品牌部署的 MVC 代码库。为了满足每个客户的需求,我们设置了可覆盖的控制器和视图。

路线注册看起来像这样

protected void Application_Start()
    {
        string deploymentName = GetDeploymentName();
        RouteConfig.RegisterRoutes(RouteTable.Routes, deploymentName);
    }

在 RegisterRoutes 中,我们首先在部署命名空间中寻找匹配的控制器,如果没有找到,则在基本命名空间中查找。

如果我们为每个部署都设置了单独的 IIS 应用程序,这可以正常工作,因为每个部署都会执行自己的 Application_Start()

但是,我们正在尝试在一个 IIS 站点实例中运行所有部署。这意味着 Application_Start() 只被调用一次,只为第一次部署注册路由。

我正在尝试将 RegisterRoutes 从 Application_Start() 移至 Session_Start() 或 Application_PreRequestHandlerExecute()

protected void Application_PreRequestHandlerExecute()
    {
        string deploymentName = GetDeploymentName();

        using (RouteTable.Routes.GetWriteLock())
        {
            RouteTable.Routes.Clear();
            RouteConfig.RegisterRoutes(RouteTable.Routes, deploymentName);
        }
    }

使用此代码,每个请求都会获取当前部署命名空间,清除路由表,并使用新命名空间再次注册路由。

2个问题:

1:请求仍然根据之前的路由执行。所以我在生命周期的错误部分,因为此时的请求已经注定了特定的控制器和操作。

2:下一个请求会抛出异常:

异常详细信息:System.InvalidOperationException:找到与名为“Home”的控制器匹配的多种类型。如果服务此请求的路由 ('{controller}/{action}/{id}') 未指定命名空间来搜索与请求匹配的控制器,则可能会发生这种情况。如果是这种情况,请通过调用采用“namespaces”参数的“MapRoute”方法的重载来注册此路由。

即使我取消了之前注册的路由,应用程序似乎仍然挂在之前注册的控制器上,现在我已经注册了另一个到另一个同名控制器的路由,这两个控制器之间存在歧义。

我的两个主要问题是:

1. 除了 Application_Start() 之外,是否还有其他生命周期事件可用于在请求已解析为先前存在的路由之前注册新路由?

2. 如何彻底清除路由和控制器,以免之前注册的控制器出现歧义异常?

4

2 回答 2

2

我做错了……我不应该担心路由,我应该担心控制器实例化。我发现您可以在 Application_Start() 中将 controllerFactory 替换为您自己的自定义工厂:

ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory(domainNamespaces));

所以在这里,我将带有命名空间值的域键字典传递给我的自定义控制器工厂中的构造函数

然后在工厂中我重写了查找控制器类型的方法

public class CustomControllerFactory : DefaultControllerFactory
{
    public Dictionary<string, string> DomainNamespaces { get; set; }

    public CustomControllerFactory(Dictionary<string, string> domainNamespaces)
        : base()
    {
        DomainNamespaces = domainNamespaces;
    }

    protected override Type GetControllerType(RequestContext requestContext, string controllerName)
    {

        controllerName += controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ? String.Empty : "Controller";

        string requestDomain = requestContext.HttpContext.Request.Url.Host;
        string defaultNamespace = "API.Controllers.Base";
        string deploymentNamespace = "API.Controllers.Deployments." + DomainNamespaces[requestDomain];

        Assembly asm = Assembly.GetExecutingAssembly();
        Type deploymentMatch = null;
        Type defaultMatch = null;

        foreach (Type type in asm.GetTypes())
        {
            if (type.Namespace == deploymentNamespace && type.Name.Equals(controllerName, StringComparison.OrdinalIgnoreCase))
            {
                deploymentMatch = type;
            }
            else if (type.Namespace == defaultNamespace && type.Name.Equals(controllerName, StringComparison.OrdinalIgnoreCase))
            {
                defaultMatch = type;
            }
        }


        return deploymentMatch ?? defaultMatch;
    }
}
于 2012-08-16T17:59:07.883 回答
1

如果您在一个实例中运行所有内容并且两个用户正在打开会话,那么在他们都在浏览您的站点时应该保持注册哪个路由?

您可以编写处理当前上下文的自定义路由,而不是在上下文更改时注册新路由:MVC 3 根据请求参数使用不同的控制器

于 2012-08-16T15:36:07.020 回答