【问题标题】:Why is ASP.NET Core's Startup class not an interface or abstract class?为什么 ASP.NET Core 的 Startup 类不是接口或抽象类?
【发布时间】:2018-11-12 00:49:45
【问题描述】:

这是关于此处解释的 Startup 类背后的设计原则:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-2.1

我了解该类需要包含ConfigureServicesConfigure 等方法。

为什么CreateDefaultBuilder(args).UseStartup<Startup>() 不强制要求任何基类或接口以提高可读性?

使用这种设计方法,必须有人阅读文档并了解诸如ConfigureServicesConfigure 之类的魔术方法名称。

如果这是一种新的类设计思维方式的一部分,那么我可以在哪里阅读更多关于它的信息?

【问题讨论】:

  • “魔术名称”是您必须自己创建的名称,并且没有记录在案。这允许您在每个环境中拥有一个 Configure/ConfigureServices,而这在包级别是未知的。抱歉,但这似乎是在咆哮。
  • 公约。我认为它允许更大的灵活性,并且需要开发人员做更少的工作。取ConfigureServices,默认为void。如果使用其他 DI 容器,则需要从 ConfigureServices 返回 IServiceProvider。您将如何使用基类来做到这一点?总是要退货吗?此外,默认情况下,当您在 Visual Studio 中创建 ASP.NET Core 项目时,将为您创建这些方法。
  • 请解释接口或抽象类如何缓解阅读文档的问题。
  • @CodingYoshi,对不起,没有关注。你能详细说明一下你的评论吗?
  • @CamiloTerevinto,关于您的“这允许您在每个环境中拥有一个 Configure/ConfigureServices” - 您能否详细说明一下或让我参考解释的文档

标签: c# design-patterns asp.net-core class-design


【解决方案1】:

这样做有几个原因。 比较明显的原因之一是,因为可以将服务注入到Configure 方法中,比如

public void Configure(IAppBuilder app, IMyService myService)
{
    myService.DoSomething();
}

显然,您不能使用接口、抽象类或继承来做到这一点。

按约定方式完成的第二个原因是,不仅有Configure/ConfigureServices 方法,还有无数种依赖于环境的配置方法。

public void Configure(IAppBuilder app) { }
public void ConfigureDevelopment(IAppBuilder app) { }
public void ConfigureProduction(IAppBuilder app) { }
public void ConfigureStaging(IAppBuilder app) { }
public void ConfigureSomethingElse(IAppBuilder app) { }

根据ASPNET_ENVIRONMENT 的环境变量,将选择并执行不同的方法(如果未找到匹配的环境特定方法,则使用默认的Configure/ConfigureServices)。

这对于传统的 OOP(继承/接口/抽象类)来说是不可能的。

这同样适用于 ASP.NET Core 的其他部分,例如中间件和Invoke 方法。 Invoke 方法也可以注入依赖项,但为了调用下一个中间件,您只需这样做

await next?.Invoke();

并且不必担心下一个中间件需要或可能需要哪些依赖项。

为了完整起见,还可以有多个 Startup 类,其默认方法名称 (Configure/ConfigureServices) 命名为 StartupDevelopmentStartupProductionStartup(作为后备)和 ASP。 NET Core 将根据环境变量集选择正确的。

【讨论】:

  • 你提到的一切都可以通过接口和/或继承来完成。我不知道你为什么认为它不可能。
  • @CodingYoshi:好吧,告诉我你如何创建一个接受无限数量和类型参数组合的接口,哈哈。当然,以 strong typed 方式,意味着 param object[] 不适合在这里,因为您不能以这种方式定义依赖关系。请记住,我们在这里讨论的是接口和抽象类上的方法,而不是作为实现细节的构造函数注入
  • 我认为首先不需要创建具有无限数量和类型参数组合的接口。如果可以通过构造函数提供依赖项,为什么还要将服务注入到 Configure 中。在这种情况下,可以将 Startup 类抽象为接口。
  • @VitaliyMarkitanov: 不会起飞 ;) 当 IoC 在 ConfigureServices 中配置并且容器仅在 ConfigureServices 被调用后创建时,你是如何将一个类注入 Startups 构造函数的在调用 Configure 之前。您无法获得当前系统的灵活性。此外,使用ConfigureServices{Environmentname} 的约定是行不通的,Start{Environmentname} 也行不通,与当前系统相比,您将完全一无所获
  • @Tseng。在 ConfigureService() 之前调用的启动构造函数。设置断点进行确认。 Startup 类也可能具有注入依赖项的构造函数——这意味着 DI 在 Startup 类创建之前被初始化。在这种情况下,类 deps 可以进入 const(这是众所周知的模式)。在 C#(和其他强类型语言)中,拥有接口和抽象类是标准方法。在 .Net Core Startup 约定中使用了特定的方法签名......这是无类型语言的影响。所以类可以通过接口或抽象抽象出来,不需要重载。
【解决方案2】:

Startup类可以从IStartup接口继承。

// \packages\microsoft.aspnetcore.hosting.abstractions\2.2.0\lib\netstandard2.0\Microsoft.AspNetCore.Hosting.Abstractions.dll
namespace Microsoft.AspNetCore.Hosting
{
 public interface IStartup
  {
   IServiceProvider ConfigureServices(IServiceCollection services);
   void Configure(IApplicationBuilder app);
  }
}

默认情况下,向导不会使用 IStartup 的实现创建模板文件。为什么不 - 可能是非类型化语言的错误或影响..

【讨论】:

  • 阅读我的回答,你就会明白为什么。使用接口你不能做void Configure(IApplicationBuilder app, IMyService myService),因为接口是一个合约,它告诉参数的确切数量、类型和顺序。你也不能叫它ConfigureDevelopment,这样它只会在ASPNETCORE_ENVIRONMENT设置为“开发”时被调用
  • 如果这样做,您将丢失特定于环境的设置,并且您还将丢失 Configure/ConfigureXxx 方法中的注入,所以不:使用当前的功能集和灵活性,作为接口是不可能的。您要么最终得到半打接口(@98​​7654326@ 和等效的 IConfigureXxx 接口)并且仅限于 3 个预定义环境,要么具有重复的设置代码(多个 Startup 类)。与当前的方法相比,从空项目开始没有哪个“更容易”
  • 原来的问题是:“为什么 ASP.NET Core 的 Startup 类不是接口或抽象类?”答:可以抽象为接口。如果您的 Startup 类是抽象的(从 IStartup 继承),那么您的 Configure(otherParams) 重载当然不会被调用。但是无论如何您都不需要,因为您在 ConfigureServices() 中注册的服务可以通过引用私有变量在 Configure(IApplicationBuilder) 中使用。再次指出,在类型化语言中的想法是使用类型,而不是反射,因为它们在 DI 中使用 javascript 或 angular 进行。那是最初的问题。
  • 不,您的答案是“是否可以作为接口实现”问题(没有被问到),问题是“为什么不这样做”。答案是“它没有完成,因为设置不灵活,并且当前的方法具有更灵活的环境相关设置”。请阅读整个问题:为什么CreateDefaultBuilder(args).UseStartup<Startup>() 不强制要求任何基类或接口以获得更好的可读性?原因很简单:因为这会限制配置它的方式。该类可以是 Startup/StartupXxx 中的任何一个,上面有任何方法
猜你喜欢
  • 1970-01-01
  • 2012-08-20
  • 2013-09-21
  • 1970-01-01
  • 2011-05-22
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 1970-01-01
相关资源
最近更新 更多