14

我在我的 Asp.Net MVC 4 项目中无法解决 404 响应。它内置于针对 4.5 的 VS2012 中。

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

控制器和视图的背景

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

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

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

加载 DLL

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

我有一个Startup有两个方法的类,PreStart并分别使用和PostStart调用这些方法。WebActivator.PreApplicationStartMethodWebActivator.PostApplicationStartMethod

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

  • 获取我的“插件”目录中所有插件 DLL 的列表
  • 将所有插件复制到AppDomain.CurrentDomain.DynamicDirectory
  • 加载类型...如果它包含一个IPluginI 然后
    • 将程序集添加到 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定义了一个名为RegisterRouteswhere 的方法,你猜对了,为实现接口的人注册路由。我调用此方法PreStart并添加路由 - 我已验证这些存在于我的路由表中。例如,在我的插件中定义的路由上,通过在 期间动态调用方法创建,PreStart在检查我的路由时,我看到这样的东西作为 DataToken:

Namespaces = Plugin.Name.Controllers

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

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

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

最后,一个问题

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

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

4

3 回答 3

8

原来这是 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如上。

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

希望这对其他人有帮助。

干杯。

于 2013-03-04T16:29:03.907 回答
1

看起来像这个问题:

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

文章在这里

于 2013-02-21T17:49:25.813 回答
0

@MisterJames,看看这个:

Asp.Net Mvc 可插拔应用程序

我希望它有用。

于 2013-02-21T19:21:14.577 回答