外部组件中的控制器上的404

时间:2013-02-15 19:26:37

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

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

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

控制器和视图的背景

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

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

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

加载DLL

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

我有一个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期间动态调用方法创建的路径,在检查我的路由时,我看到像DataToken这样的东西:

Namespaces = Plugin.Name.Controllers

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

这是让我感到困惑的部分:如果在这一点上没有做任何事情,我会回到Visual Studio并点击F5 ......一切正常。

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

最后一个问题

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

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

3 个答案:

答案 0 :(得分:5)

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

在与Asp.Net团队的Eilon Lipton讨论这个问题后,并认为这在MVC框架中有些不妥,Eilon和几个团队成员挖掘了一些东西,发现每个对话的错误都在较低的水平:http://aspnetwebstack.codeplex.com/discussions/403529

他们还提出了一种解决方法,其中包括在调用BuildManager后对AddReferencedAssembly的另一个调用,我通过以下代码实现了该调用:

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

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

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

希望这有助于其他人。

干杯。

答案 1 :(得分:1)

看起来像这个问题:

  

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

文章是here

答案 2 :(得分:0)

@MisterJames,看看这个:

Asp.Net Mvc Pluggable Application

我希望它有用。