【问题标题】:404 on Controllers in External Assemblies404 关于外部组件中的控制器
【发布时间】:2013-01-31 20:30:00
【问题描述】:

我在解决我的 Asp.Net MVC 4 项目中的 404 响应时遇到问题。它内置于针对 4.5 的 VS2012。

我在独立的 DLL 中内置了预编译的视图和控制器。我能够动态加载 DLL 并从我的核心项目中检查它们,甚至调用它们的方法;但是,似乎 MVC 框架不知道控制器。我在这里很近,但缺少一些东西。

控制器和视图的背景

控制器构建在独立的 MVC 项目中,并继承自 Controller。那里没有什么太有趣的事情发生。视图使用 RazorGenerator 并成为项目中的类。

项目的输出是一个正确包含控制器和视图的 DLL。

DLL 实现了一个特定的接口,我们称之为IPlugin,在库中的一个单独的类(不是控制器的一部分)中。

加载 DLL

在 Visual Studio 中以管理员身份运行我编译了托管在 IIS 下的应用程序。构建项目后,我将插件 DLL 放入我的“插件”目录。没有调试(这在以后变得很重要),我打开 IE 并导航到该站点。 请注意,此时应用程序已构建,但从未运行,因此将触发启动事件。如果我回收应用程序池,这里的一切仍然是一致的。

我有一个Startup 类,它有两个方法PreStartPostStart,并分别使用WebActivator.PreApplicationStartMethodWebActivator.PostApplicationStartMethod 调用这些方法。

PreStart 是我执行以下操作的地方:

  • 获取我的“插件”目录中所有插件 DLL 的列表
  • 将所有插件复制到AppDomain.CurrentDomain.DynamicDirectory
  • 加载类型...如果它包含IPlugin 然后
    • 将程序集添加到 BuildManager
    • 在实现IPlugin的类上调用一些方法

在“PostStart”中,我编写了这段代码(基于来自 RazorGenerator.Mvc 的代码):

foreach (var assembly in Modules.Select(m=>m.Value))
{
    var engine = new PrecompiledMvcEngine(assembly)
    {
        UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal
    };

    ViewEngines.Engines.Insert(0, engine);
    VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
}

Modules 在此上下文中是一个键/值对,其中的值是加载的程序集。这段代码的目的是通过为每个知道如何解析视图的程序集添加一个视图引擎来确保 MVC 知道视图(这是 RazorGenerator 的一部分)。

我怎么知道我很接近(但显然缺少雪茄)

IPlugin 定义了一个名为RegisterRoutes 的方法,您猜对了,路由将为实现该接口的人注册。我在PreStart 中调用此方法并添加路由 - 我已验证这些存在于我的路由表中。例如,在我的插件中定义的路由上,通过在PreStart 期间动态调用该方法创建,在检查我的路由时,我看到类似这样的数据令牌:

Namespaces = Plugin.Name.Controllers

所以,路由已注册,程序集已加载,我已验证 DLL 已正确复制到 AppDomain 的 DynamicDirectory。我能够调用在运行时动态加载的类的成员。但是当我导航到与路由匹配的 URL 时,我得到了 404。这不是“找不到视图”的 YSOD,更像是根本找不到控制器。

这是让我感到困惑的部分:如果此时我什么都不做,返回 Visual Studio 并按 F5...一切正常。

就像 Visual Studio 以某种我无法识别的方式意识到控制器,而 MVC 框架正在接受它。

最后一个问题

我缺少什么,如何让 MVC 框架了解我的控制器?

嘿,在这一点上,如果你还在读这篇文章,谢谢。 :)

【问题讨论】:

  • 1. VS 是否使用 Cassini 运行?尝试将其更改为 IIS Express 并检查它是否继续正常运行。 2. 尝试安装RouteDebugger - 也许它可能会给你一些关于路由是否在IIS下正确注册的线索
  • 感谢@Pranav,但它已经在 IIS 上。路由调试器显示路由正在运行。
  • 这可能是个问题吗? stackoverflow.com/questions/14971895/…
  • +1,但不,还没有,反正。框架没有找到控制器,所以还没有任何东西在寻找视图。而且,当我重新编译时,一切正常。

标签: .net asp.net-mvc asp.net-mvc-4 assemblies


【解决方案1】:

看起来像这个问题:

MVC 使用视图引擎的程序集限定类型名称来 消除来自不同视图引擎的视图缓存条目的歧义。 原来如此 不可能有多个 PrecompiledMvc​​Engine 对象(如 当您在多个程序集中预编译视图时)。这 可以通过创建不同的派生类来解决问题 每个程序集的 PrecompiledMvc​​Engine。或者通过创建一个 用程序集中的某种类型参数化的泛型派生类。

文章是here

【讨论】:

  • 感谢 Nenad,但此时我只运行一个插件,所以只有一个试图加载。这可能是一个因素,但我也同时运行了 2 个插件,所以我不确定它会不会。此外,这并不能解决我在找不到控制器时遇到的问题。这是赏金的关键。干杯。
  • 我希望有。当我尝试访问视图时,我得到的只是控制器上的 404。我有 Log4Net 和 ELMAH,我没有发现任何问题(每一步都记录)。在本周与 Asp.Net 团队的一些成员讨论了一些事情后,我将在稍后添加到问题中的更多细节。
【解决方案2】:

@MisterJames,看看这个:

Asp.Net Mvc Pluggable Application

希望对你有用。

【讨论】:

  • Felipe,这是在同一解决方案中使用项目中的区域,是一种完全不同的方法。我的需求是为插件提供独立的解决方案。我将对此进行更详细的评估,看看是否有任何线索,但就目前而言,我认为这对我的方案没有帮助。
【解决方案3】:

原来这是 Asp.Net 本身的一个错误。

在与 Asp.Net 团队的 Eilon Lipton 讨论了这个问题后,认为这是 MVC 框架中的问题,Eilon 和几个团队成员深入研究并发现在这次谈话中错误处于较低级别:http://aspnetwebstack.codeplex.com/discussions/403529

他们还提出了一种解决方法,其中包括在调用 AddReferencedAssembly 之后再次调用 BuildManager,这是我通过以下代码实现的:

    // Add the plugin as a reference to the application
    BuildManager.AddReferencedAssembly(assembly);
    BuildManager.AddCompilationDependency(assembly.FullName);

这允许您在应用程序前的初始化阶段在启动时添加额外的控制器/编译视图。我现在正在做的是遍历我的插件目录中的 DLL 列表并将它们推送到BuildManager,如上所述。

这里唯一的限制是您不能删除程序集或动态清除缓存。我发现这样做的唯一方法是将以前未知的程序集添加到引用的程序集和编译依赖项中。我正在尝试在预应用程序初始化期间动态发出一个新程序集,这样我就可以始终有效地清除缓存并通过伪造一个新程序集来删除以前包含的插件。

希望这对其他人有所帮助。

干杯。

【讨论】:

  • 非常感谢您详细的问答。我不明白的一件事是您为什么不使用自定义控制器工厂(请参阅ControllerBuilder.Current.SetControllerFactory)?这将使您可以完全控制何时让出哪个控制器,不是吗?
  • 我尝试使用控制器工厂,但它不起作用(404 错误),但在调试的 Visual Studio 中完美运行。有了这个解决方案,它就可以工作了。
猜你喜欢
  • 2014-08-14
  • 1970-01-01
  • 2015-12-16
  • 2018-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 1970-01-01
相关资源
最近更新 更多