覆盖/扩展MVC控制器/区域

Tho*_*son 8 c# asp.net asp.net-mvc

我正在研究一个MVC项目,我正在试图弄清楚如何扩展区域内现有Controller的路由,特别是从另一个项目.

例如,我有一个控制器,其区域如下所示:

namespace MyProject.Areas.Foo.Controllers
{
    [Authorize]
    public class FooController : ApplicationController
    {
          //code
    }
}
Run Code Online (Sandbox Code Playgroud)

而我想做的是,能够在一个单独的项目中定义另一个Controller,可以像这样扩展它:

namespace MyOtherProject.Areas.Foo.Custom.Controllers
{
    public class FooController : ApplicationController
    {
          public string Bar()
          {
               return "Bar";
          }
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上,我希望控制器几乎像我使用partial关键字一样运行(这样我就可以调用原始或新的操作中的任何操作).

主要问题

我真正想要实现的是,我有一个主要项目,包括几个区域和我的解决方案的另一个区域,包含各种客户端文件夹.我希望能够为我的主项目扩展基本控制器,并在这些客户端文件夹中添加特定于客户端的操作,以便它们可以在主项目中使用.我已经使用某些MVC视图执行此操作,但我希望我也可以使用控制器完成它.

我试过的

  • 我尝试partial在类的两个声明中使用关键字,但由于它们在不同的项目/程序集中,我认为这不起作用.
  • 我定义了一个构建事件,它将自定义DLL移动到bin主MVC项目的目录中,但似乎没有按预期工作.
  • 我已经尝试了各种继承方法,希望新类可以被拾取,但那些不起作用(收到重复的控制器声明错误).
  • 我读过关于尝试使用自定义ControllerFactory但我不确定如何实现它.
  • 我已经尝试在AreaRegistration部分中定义自定义命名空间路由参数来获取新控制器,如下例所示.

路由示例(AreaRegistration)

context.MapRoute(
    AreaName,
    String.Format("{0}/{{action}}/{{id}}", AreaName),
    new { controller = AreaName, action = "Index", id = UrlParameter.Optional },
    new[] { 
        String.Format("MyProject.Areas.{0}.Controllers", AreaName),
        String.Format("MyOtherProject.Areas.{0}.Custom.Controllers", AreaName)
    }
);
Run Code Online (Sandbox Code Playgroud)

更新

我尝试了一种在这里看到的方法,根据一些评论讨论,只需通过继承处理这个:

// Main Project
namespace MyProject.Areas.Foo.Controllers
{
    [Authorize]
    public class FooController : ApplicationController
    {
          public ActionResult Index()
          {
              return View();
          }
    }
}

// This is in another project / namespace / assembly
namespace MyOtherProject.Foo.Controllers
{
    public class CustomFooController : MyProject.Areas.Foo.Controllers.FooController
    {
        [Route("Foo/Bar")]
        public string Bar()
        {
            return "Bar";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我目前的步骤如下:

  • FooController另一个项目/解决方案中的主项目中的基础继承.
  • 设置属性路由以访问自定义控制器,以避免来自主项目的路由冲突.
  • 创建了一个构建事件,可以在新的自定义项目构建时将自定义DLL移动到主项目中(因此可以访问它).

这似乎没有任何区别.我试着去了Foo/Bar网址,但它只是扔了一个404就好像根本没看到它一样.该CustomFooController.cs文件位于它自己的独立项目中,只是一个类文件而不是MVC项目.它是否正确?我是否需要在主项目中设置路由规则?

Rio*_*ams 7

控制器继承

使用继承作为Chris在评论部分提到的可能也是最好的解决方法.如果您已经从ApplicationController示例中的另一个基本控制器类派生,则尤其如此:

// ProjectA is assumed to be your "main" MVC application
public class CustomFooController :  ProjectA.Controllers.FooController
{
    [Route("Foo/Bar")]
    public ActionResult Bar()
    {
        return Content("Bar");
    }
}
Run Code Online (Sandbox Code Playgroud)

此处的属性路由非常重要,因为您不希望现有路由混淆两个控制器或忽略它们.

注册属性路由

由于您通过[Route]ProjectB部分中的属性使用属性路由,因此您需要确保在RouteConfig.csProjectA项目中明确设置它,以便它可以通过以下Routes.MapMvcAttributeRoutes()方法正确识别它:

public static void RegisterRoutes(RouteCollection routes)
{
    // This is important to set up your Route Attributes
    routes.MapMvcAttributeRoutes();

    // Route declarations omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

同样,如果您正在使用区域,您还需要在相应的AreaRegistration.cs文件中配置它:

public override void RegisterArea(AreaRegistrationContext context) 
{
    // Wire up any attribute based routing
    context.Routes.MapMvcAttributeRoutes();

    // Area routing omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

确定路线

最后,您要确保做的最后一件事是正确地"定位"您的路由,以便在RouteConfig.cs主ProjectA应用程序中确定主命名空间的优先级:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapMvcAttributeRoutes();
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Foo", action = "Index", id = UrlParameter.Optional },
        // This will prioritize your existing Controllers so they work as expected  
        namespaces: new[] { "ProjectA.Controllers"}
    );
}
Run Code Online (Sandbox Code Playgroud)

获得参考资料

您提到使用构建事件将DLL从ProjectB项目复制到您的主ProjectA项目中,在这种情况下应该没问题.你基本上需要一些方法来访问它,xcopy在大多数情况下,如下所示应该没问题:

xcopy /E /Y /S  "$(ProjectName).dll" "$(SolutionDir)\ProjectA\Bin\"
Run Code Online (Sandbox Code Playgroud)

把它放在一起

如果您已正确连接所有这些步骤,则应该能够清理/重建现有解决方案.执行此操作后,请仔细检查以确保在ProjectA bin目录中具有相应的DLL :

在此输入图像描述

如果那样,那么您就在正确的轨道上,应该能够运行您的主应用程序并导航到~/Foo以下内容:

在此输入图像描述

同样,导航到~/Foo/Bar应该选择在其他Controller中定义的适当属性路由并提供适当的内容:

在此输入图像描述