ASP.NET 'public' (Core) 的僵尸病毒:控制器可以是内部的吗?
ASP.NET (Core)'s zombie virus of 'public': can controllers be internal?
上下文是ASP.NET核心。
请考虑以下最小设置。
public class ExampleController : ControllerBase
{
public ExampleController(IExempleService exampleService)
{
(...)
public interface IExampleService
{
ExampleStruct Foo();
}
public readonly struct ExampleStruct (...)
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IInverterService, InverterService>();
这里有一个 publicity 链。
- 控制器必须是public;被发现。 一般。
- 控制器的构造函数必须是public;让框架实例化它。 这就是生活。
- 服务接口必须是public,因为控制器的构造函数是public。 悲伤。
- 服务类型(例如 ExampleStruct)必须是
public
,因为接口是 public。 我觉得恶心。
这样做的实际后果是:
- 文档,我正在记录实际上内部但技术上 public 类 毫无意义的评论,这些评论将变得过时。
- Public 类型有更多的责任。例如,一个内部结构不需要提供相等性,但当它是 public 时它应该。
我想这里有两个问题:
- 这种 public 访问的僵尸病毒最终会有结构性解决方案吗?
- 我是不是做错了,现在有好的解决方案吗?
单元测试和 internal
与 InternalsVisibleToAttribute 相结合。
所有功劳应归功于@MichaelRandall:
You can make your controllers internal, you will just have to make them discoverable with ControllerFeatureProvider take a look at this whosebug.com/a/50906593/1612975 – Michael Randall
public void ConfigureServices(IServiceCollection services)
{
(...)
services
.AddControllers(options => ...)
.ConfigureApplicationPartManager(manager =>
{
manager.FeatureProviders.Add(new MyControllerFeatureProvider());
})
(...)
internal class MyControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
// https://source.dot.net/#Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeatureProvider.cs,41
// contains
//
// // We only consider public top-level classes as controllers. IsPublic returns false for nested
// // classes, regardless of visibility modifiers
// if (!typeInfo.IsPublic)
// {
// return false;
// }
if (typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
typeInfo.IsDefined(typeof(ApiControllerAttribute)))
{
System.Diagnostics.Debug.WriteLine($"Found controller '{typeInfo.Name}'");
return true;
}
if (typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
{
System.Diagnostics.Debug.Fail($"Found class that ends in 'Controller' but will not be registered as a controller: {typeInfo.Name}");
}
return false;
}
}
你可以制作你的控制器internal
,你将拥有使它们 可发现 ControllerFeatureProvider
Discovers controllers from a list of ApplicationPart
instances.
在 Asp.Net Core 中有 4 种定义控制器的方式:
- 继承自ControllerBaseclass
- 定义一个名称包含“Controller”后缀的控制器。
- 使用控制器属性定义
- 定义自定义控制器类型Class
对您的 internal
控制器情况最重要的是最后一点(定义自定义控制器类型)。
要做到这一点,您需要
- 实施
ControllerFeatureProvider
- 覆盖
IsController
并实施任何逻辑以提高您的类型的可发现性
例如
internal class MyControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
...
- 通过
FeatureProviders
将其添加到 ConfigureApplicationPartManager
例如
.ConfigureApplicationPartManager(manager =>
{
manager.FeatureProviders.Add(YourControllerFeatureProvider)
...
上下文是ASP.NET核心。
请考虑以下最小设置。
public class ExampleController : ControllerBase
{
public ExampleController(IExempleService exampleService)
{
(...)
public interface IExampleService
{
ExampleStruct Foo();
}
public readonly struct ExampleStruct (...)
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IInverterService, InverterService>();
这里有一个 publicity 链。
- 控制器必须是public;被发现。 一般。
- 控制器的构造函数必须是public;让框架实例化它。 这就是生活。
- 服务接口必须是public,因为控制器的构造函数是public。 悲伤。
- 服务类型(例如 ExampleStruct)必须是
public
,因为接口是 public。 我觉得恶心。
这样做的实际后果是:
- 文档,我正在记录实际上内部但技术上 public 类 毫无意义的评论,这些评论将变得过时。
- Public 类型有更多的责任。例如,一个内部结构不需要提供相等性,但当它是 public 时它应该。
我想这里有两个问题:
- 这种 public 访问的僵尸病毒最终会有结构性解决方案吗?
- 我是不是做错了,现在有好的解决方案吗?
单元测试和 internal
与 InternalsVisibleToAttribute 相结合。
所有功劳应归功于@MichaelRandall:
You can make your controllers internal, you will just have to make them discoverable with ControllerFeatureProvider take a look at this whosebug.com/a/50906593/1612975 – Michael Randall
public void ConfigureServices(IServiceCollection services)
{
(...)
services
.AddControllers(options => ...)
.ConfigureApplicationPartManager(manager =>
{
manager.FeatureProviders.Add(new MyControllerFeatureProvider());
})
(...)
internal class MyControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
// https://source.dot.net/#Microsoft.AspNetCore.Mvc.Core/Controllers/ControllerFeatureProvider.cs,41
// contains
//
// // We only consider public top-level classes as controllers. IsPublic returns false for nested
// // classes, regardless of visibility modifiers
// if (!typeInfo.IsPublic)
// {
// return false;
// }
if (typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
typeInfo.IsDefined(typeof(ApiControllerAttribute)))
{
System.Diagnostics.Debug.WriteLine($"Found controller '{typeInfo.Name}'");
return true;
}
if (typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase))
{
System.Diagnostics.Debug.Fail($"Found class that ends in 'Controller' but will not be registered as a controller: {typeInfo.Name}");
}
return false;
}
}
你可以制作你的控制器internal
,你将拥有使它们 可发现 ControllerFeatureProvider
Discovers controllers from a list of
ApplicationPart
instances.
在 Asp.Net Core 中有 4 种定义控制器的方式:
- 继承自ControllerBaseclass
- 定义一个名称包含“Controller”后缀的控制器。
- 使用控制器属性定义
- 定义自定义控制器类型Class
对您的 internal
控制器情况最重要的是最后一点(定义自定义控制器类型)。
要做到这一点,您需要
- 实施
ControllerFeatureProvider
- 覆盖
IsController
并实施任何逻辑以提高您的类型的可发现性
例如
internal class MyControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
...
- 通过
FeatureProviders
将其添加到
ConfigureApplicationPartManager
例如
.ConfigureApplicationPartManager(manager =>
{
manager.FeatureProviders.Add(YourControllerFeatureProvider)
...