ASP.NET(核心)的“公共”僵尸病毒:控制器可以位于内部吗?

时间:2020-10-02 01:38:49

标签: c# .net asp.net-core dependency-injection scope

上下文是ASP.NET Core。

请考虑以下最小设置。

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>();

这里有一个宣传链。

  1. 控制器必须是公共的;被发现。 一般。
  2. 控制器的构造函数必须是公共的;实例化它的框架。 那就是生活。
  3. 服务接口必须是公共的,因为控制器的构造函数是公共的。 悲伤。
  4. 由于接口是公共的,因此服务类型(例如ExampleStruct)必须为public我感到恶心。

这有实际后果:

  • 文档,我正在记录实际上是内部的但在技术上是公开的类,其中带有毫无意义的注释,这些注释将过时。
  • 公共类型负有更多责任。例如,内部结构不需要提供相等性,但是当它公开时应该提供平等性。

我想这里有两个问题:

  • 最终将为这种僵尸病毒公众获得结构解决方案吗?
  • 我做错了吗,现在有一个好的解决方案?

单元测试和internalInternalsVisibleToAttribute 一起使用。

2 个答案:

答案 0 :(得分:2)

所有功劳应归功于@MichaelRandall:

可以将控制器设置为内部控制器,只需使用ControllerFeatureProvider使它们可被发现,请查看此stackoverflow.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;
    }
}

答案 1 :(得分:1)

可以 使您的控制器 internal,只需将其设置为可发现ControllerFeatureProvider

ApplicationPart实例列表中发现控制器。

Asp.Net Core中有4种方式定义控制器:

  1. 继承自ControllerBase类
  2. 定义一个名称后缀为“ Controller”的控制器。
  3. 使用控制器属性定义
  4. 定义自定义控制器类型类

最后一点(定义自定义控制器类型)对您来说internal控制器的情况最为重要。

为此,您将需要

  • 实施ControllerFeatureProvider
  • 覆盖IsController并为发现类型实施任何逻辑

例如

internal class MyControllerFeatureProvider : ControllerFeatureProvider
{
    protected override bool IsController(TypeInfo typeInfo)
    {
        ...
  • 通过ConfigureApplicationPartManager将其添加到FeatureProviders

例如

.ConfigureApplicationPartManager(manager =>
{
    manager.FeatureProviders.Add(YourControllerFeatureProvider)
    ...