如何在 ASP.NET Core MVC 2.0 中的另一个程序集中使用控制器?

问题描述 投票:0回答:6

为了模块化,我在不同的组件中创建了一些控制器。每个程序集代表整个系统的一个有界上下文(模块、子系统、部门等)。

每个模块的控制器都是由对其他模块一无所知的人开发的,而中央编排器将在一个应用程序中涵盖所有这些模块。

所以,有一个叫做 school 的模块,里面有一个

TeacherController
。它的输出是
Contoso.School.UserService.dll

主要编排器称为

Education
,它引用了
Contoso.School.UserService.dll

我的

program.cs
是:

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args).UseKestrel()
            .UseStartup<Startup>()
            .Build();

但是对于教师控制器的路线,我得到404。如何在其他程序集中使用控制器?

c# asp.net-core-mvc
6个回答
93
投票

ConfigureServices
类的
Startup
方法中,您必须调用以下内容:

services.AddMvc().AddApplicationPart(assembly).AddControllersAsServices();

其中

assembly
是代表
Assembly
的实例
Contoso.School.UserService.dll

您可以从任何包含的类型获取它或像这样加载它:

var assembly = Assembly.Load("Contoso.School.UserService");

69
投票

对于 .NET Core 3.0 API 已略有更改,从外部程序集注册控制器的最简单方法是

Startup.cs
如下所示:

public void ConfigureServices(IServiceCollection services)
{
    var assembly = typeof(**AnyTypeFromRequiredAssembly**).Assembly;

    services.AddControllers()
        .PartManager.ApplicationParts.Add(new AssemblyPart(assembly));
}

5
投票

我正在尝试解析控制器,同时将遗留单元测试从 .NET Framework 迁移到 aspnet core 3.1,这是我让它工作的唯一方法:

var startupAssembly = typeof(Startup).Assembly;

var services = new ServiceCollection();

// Add services
...

// Add Controllers
services
    .AddControllers()
    .AddApplicationPart(startupAssembly)
    .AddControllersAsServices();

如果我更改最后三行的顺序,它将不起作用。


避免在单元测试中执行此操作,除非确实必须这样做,即出于遗留原因。如果您不使用遗留代码,您可能正在寻找集成测试


3
投票

上述答案没有任何问题,我只是使用 1 衬垫,因为我知道我想要加载的外部程序集中包含的类。

以下示例来自 ASP-WAF 防火墙,用于在 .net Core 3.1 中的现有 Web 应用程序中加载报告端点和仪表板网页。

services.AddMvc(options =>
{
    options.Filters.Add<Walter.Web.FireWall.Filters.FireWallFilter>();
    options.Filters.Add<Walter.Web.FireWall.Filters.PrivacyPreferencesFilter>();
}).AddApplicationPart(Assembly.GetAssembly(typeof(Walter.Web.FireWall.DefaultEndpoints.ReportingController)));

因此,为了回答您的问题,我们假设 TeacherController 位于命名空间 Contoso.School.UserService 中,您的实现将是:

services.AddMvc(options =>
{                
    //any option
}).AddApplicationPart(Assembly.GetAssembly(typeof(Contoso.School.UserService.TeacherController )));

或者,如果您没有任何选项,则忽略它们并使用此:

services.AddMvc()
    .AddApplicationPart(Assembly.GetAssembly(typeof(Contoso.School.UserService.TeacherController)));

如果您不确定程序集中的类,那么我们会在代码中从名称空间开始智能,并查找要使用的类型或在 Visual Studio 中打开对象浏览器

如果您有任何问题,请告诉我。


0
投票

上面马丁有答案,谢谢。然而,如何将程序集传递给 Startup.ConfigureServices 并不是立即显而易见的。我是如何实现这一点的......在我创建和启动 webHost 的代码中,我调用 IWebHostBuilder.ConfigureServices 并为其提供包含程序集的内容(在我的例子中是一个名为 IOutputProcess 的自定义接口)

            _webHost = CreateWebHostBuilder().ConfigureServices(e => e.AddSingleton(_outputProcess)).Build();
            _webHost.Start();

然后在我的 Startup.ConfigureServices 中,我将该实例从 IServiceCollection 中拉回...

    public void ConfigureServices(IServiceCollection services)
    {
        var sp = services.BuildServiceProvider();
        var outputProcess = sp.GetService<IOutputProcess>();
        services.AddMvc().AddApplicationPart(outputProcess.ControllerAssembly).AddControllersAsServices();
    }

我怀疑纯粹出于此目的实例化服务提供者是最简洁的做事方式,但它确实有效。 (我愿意接受更好的想法)


0
投票

仅供参考,在 ASP.NET Core 7 中,这有效

automagically

更准确地说,事实上我的主服务器应用程序对类库有一个硬引用,该类库在

[ApiController]
文件夹中包含
Controllers
类,该控制器类上的路由刚刚工作

© www.soinside.com 2019 - 2024. All rights reserved.