为了模块化,我在不同的组件中创建了一些控制器。每个程序集代表整个系统的一个有界上下文(模块、子系统、部门等)。
每个模块的控制器都是由对其他模块一无所知的人开发的,而中央编排器将在一个应用程序中涵盖所有这些模块。
所以,有一个叫做 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。如何在其他程序集中使用控制器?
在
ConfigureServices
类的 Startup
方法中,您必须调用以下内容:
services.AddMvc().AddApplicationPart(assembly).AddControllersAsServices();
其中
assembly
是代表 Assembly
的实例 Contoso.School.UserService.dll
。
您可以从任何包含的类型获取它或像这样加载它:
var assembly = Assembly.Load("Contoso.School.UserService");
对于 .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));
}
我正在尝试解析控制器,同时将遗留单元测试从 .NET Framework 迁移到 aspnet core 3.1,这是我让它工作的唯一方法:
var startupAssembly = typeof(Startup).Assembly;
var services = new ServiceCollection();
// Add services
...
// Add Controllers
services
.AddControllers()
.AddApplicationPart(startupAssembly)
.AddControllersAsServices();
如果我更改最后三行的顺序,它将不起作用。
避免在单元测试中执行此操作,除非确实必须这样做,即出于遗留原因。如果您不使用遗留代码,您可能正在寻找集成测试。
上述答案没有任何问题,我只是使用 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 中打开对象浏览器
如果您有任何问题,请告诉我。
上面马丁有答案,谢谢。然而,如何将程序集传递给 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();
}
我怀疑纯粹出于此目的实例化服务提供者是最简洁的做事方式,但它确实有效。 (我愿意接受更好的想法)
仅供参考,在 ASP.NET Core 7 中,这有效
automagically
。
更准确地说,事实上我的主服务器应用程序对类库有一个硬引用,该类库在
[ApiController]
文件夹中包含 Controllers
类,该控制器类上的路由刚刚工作