前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NET Core 3.0之深入源码理解Startup的注册及运行

.NET Core 3.0之深入源码理解Startup的注册及运行

作者头像
Edison.Ma
发布2019-07-19 18:33:03
1.2K0
发布2019-07-19 18:33:03
举报
文章被收录于专栏:DotNet Core圈圈

写在前面

开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程序的起点。通过使用Startup,可以配置化处理所有向应用程序所做的请求的管道,同时也可以减少.NET应用程序对单一服务器的依赖性,使我们在更大程度上专注于面向多服务器为中心的开发模式。

Startup讨论

Starup所承担的角色

Startup作为一个概念是ASP.NET Core程序中所必须的,Startup类本身可以使用多种修饰符(public、protect,private、internal),作为ASP.NET Core应用程序的入口,它包含与应用程序相关配置的功能或者说是接口。

虽然在程序里我们使用的类名就是Startup,但是需要注意的是,Startup是一个抽象概念,你完全可以名称成其他的,比如MyAppStartup或者其他的什么名称,只要你在Program类中启动你所定义的启动类即可。

当然如果不想写Startup,可以在Program类中配置服务和请求处理管道,请参见评论区5楼,非常感谢Emrys耐心而又全面的指正

以下是基于ASP.NET Core Preview 3模板中提供的写法:

代码语言:javascript
复制
   1:  public class Program
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      public static void Main(string[] args)
代码语言:javascript
复制
   4:      {
代码语言:javascript
复制
   5:          CreateHostBuilder(args).Build().Run();
代码语言:javascript
复制
   6:      }
代码语言:javascript
复制
   7:   
代码语言:javascript
复制
   8:      public static IHostBuilder CreateHostBuilder(string[] args) =>
代码语言:javascript
复制
   9:          Host.CreateDefaultBuilder(args)
代码语言:javascript
复制
  10:              .ConfigureWebHostDefaults(webBuilder =>
代码语言:javascript
复制
  11:              {
代码语言:javascript
复制
  12:                  webBuilder.UseStartup<Startup>();
代码语言:javascript
复制
  13:              });
代码语言:javascript
复制
  14:  }

不管你命名成什么,只要将webBuilder.UseStartup<>()中的泛型类配置成你定义的入口类即可;

Startup编写规范

下面是ASP.NET Core 3.0 Preview 3模板中Startup的写法:

代码语言:javascript
复制
   1:  // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
代码语言:javascript
复制
   2:  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
代码语言:javascript
复制
   3:  {
代码语言:javascript
复制
   4:      if (env.IsDevelopment())
代码语言:javascript
复制
   5:      {
代码语言:javascript
复制
   6:          app.UseDeveloperExceptionPage();
代码语言:javascript
复制
   7:      }
代码语言:javascript
复制
   8:      else
代码语言:javascript
复制
   9:      {
代码语言:javascript
复制
  10:          // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
代码语言:javascript
复制
  11:          app.UseHsts();
代码语言:javascript
复制
  12:      }
代码语言:javascript
复制
  13:   
代码语言:javascript
复制
  14:      app.UseHttpsRedirection();
代码语言:javascript
复制
  15:   
代码语言:javascript
复制
  16:      app.UseRouting(routes =>
代码语言:javascript
复制
  17:      {
代码语言:javascript
复制
  18:          routes.MapControllers();
代码语言:javascript
复制
  19:      });
代码语言:javascript
复制
  20:   
代码语言:javascript
复制
  21:      app.UseAuthorization();
代码语言:javascript
复制
  22:  }

通过以上代码可以知道,Startup类中一般包括

  • 构造函数:通过我们以前的开发经验,我们可以知道,该构造方法可以包括多个对象
    • IConfiguration:表示一组键/值应用程序配置属性。
    • IApplicationBuilder:是一个包含与当前环境相关的属性和方法的接口。它用于获取应用程序中的环境变量。
    • IHostingEnvironment:是一个包含与运行应用程序的Web宿主环境相关信息的接口。使用这个接口方法,我们可以改变应用程序的行为。
    • ILoggerFactory:是为ASP.NET Core中的日志记录系统提供配置的接口。它还创建日志系统的实例。
  • ConfigureServices
  • Configure

Startup在创建服务时,会执行依赖项注册服务,以便在应用程序的其它地方使用这些依赖项。ConfigureServices 用于注册服务,Configure 方法允许我们向HTTP管道添加中间件和服务。这就是ConfigureServices先于Configure 之前调用的原因。

ConfigureServices

该方法时可选的,非强制约束,它主要用于对依赖注入或ApplicationServices在整个应用中的支持,该方法必须是public的,其典型模式是调用所有 Add{Service}方法,主要场景包括实体框架、认证和 MVC 注册服务:

代码语言:javascript
复制
   1:  services.AddDbContext<ApplicationDbContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
代码语言:javascript
复制
   2:  services.AddDefaultIdentity<IdentityUser>().AddDefaultUI(UIFramework.Bootstrap4).AddEntityFrameworkStores<ApplicationDbContext>();
代码语言:javascript
复制
   3:  services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
代码语言:javascript
复制
   4:  // Add application services.此处主要是注册IOC服务
代码语言:javascript
复制
   5:  services.AddTransient<IEmailSender, AuthMessageSender>();
代码语言:javascript
复制
   6:  services.AddTransient<ISmsSender, AuthMessageSender>();

Configure

该方法主要用于定义应用程序对每个HTTP请求的响应方式,即我们可以控制ASP.NET管道,还可用于在HTTP管道中配置中间件。请求管道中的每个中间件组件负责调用管道中的下一个组件,或在适当情况下使链发生短路。如果中间件链中未发生短路,则每个中间件都有第二次机会在将请求发送到客户端前处理该请求。

该方法接受IApplicationBuilder作为参数,同时还可以接收其他一些可选参数,如IHostingEnvironment和ILoggerFactory。

一般而言,只要将服务注册到configureServices方法中时,都可以在该方法中使用。

代码语言:javascript
复制
   1:  app.UseDeveloperExceptionPage();
代码语言:javascript
复制
   2:  app.UseHsts();
代码语言:javascript
复制
   3:  app.UseHttpsRedirection();
代码语言:javascript
复制
   4:  app.UseRouting(routes =>
代码语言:javascript
复制
   5:  {
代码语言:javascript
复制
   6:      routes.MapControllers();
代码语言:javascript
复制
   7:  });
代码语言:javascript
复制
   8:  app.UseAuthorization();

扩展Startup方法

使用IStartupFilter来对Startup功能进行扩展,在应用的Configure中间件管道的开头或末尾使用IStartupFilter来配置中间件。IStartupFilter有助于确保当库在应用请求处理管道的开端或末尾添加中间件的前后运行中间件。

以下是IStartupFilter的源代码,通过源代码我们可以知道,该接口有一个Action<IApplicationBuilder>类型,并命名为Configure的方法。由于传入参数类型和返回类型一样,这就保证了扩展的传递性及顺序性,具体的演示代码,可以参数MSDN

代码语言:javascript
复制
   1:  using System;
代码语言:javascript
复制
   2:  using Microsoft.AspNetCore.Builder;
代码语言:javascript
复制
   3:    
代码语言:javascript
复制
   4:  namespace Microsoft.AspNetCore.Hosting
代码语言:javascript
复制
   5:  {
代码语言:javascript
复制
   6:    public interface IStartupFilter
代码语言:javascript
复制
   7:    {
代码语言:javascript
复制
   8:        Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
代码语言:javascript
复制
   9:    }
代码语言:javascript
复制
  10:  }

Startup是如何注册和执行的

此段文字,只是我想深入了解其内部机制而写的,如果本身也不了解,其实是不影响我们正常编写.NET Core应用的。

UseStartup源码

ASP.NET Core通过调用IWebHostBuilder.UseStartup方法,传入Startup类型,注意开篇就已经说过Startup是一个抽象概念,我们看下源代码:

代码语言:javascript
复制
   1:  /// <summary>
代码语言:javascript
复制
   2:   /// Specify the startup type to be used by the web host.
代码语言:javascript
复制
   3:   /// </summary>
代码语言:javascript
复制
   4:   /// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
代码语言:javascript
复制
   5:   /// <param name="startupType">The <see cref="Type"/> to be used.</param>
代码语言:javascript
复制
   6:   /// <returns>The <see cref="IWebHostBuilder"/>.</returns>
代码语言:javascript
复制
   7:   public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
代码语言:javascript
复制
   8:   {
代码语言:javascript
复制
   9:       var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;
代码语言:javascript
复制
  10:   
代码语言:javascript
复制
  11:      hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName);
代码语言:javascript
复制
  12:   
代码语言:javascript
复制
  13:      // Light up the GenericWebHostBuilder implementation
代码语言:javascript
复制
  14:      if (hostBuilder is ISupportsStartup supportsStartup)
代码语言:javascript
复制
  15:      {
代码语言:javascript
复制
  16:          return supportsStartup.UseStartup(startupType);
代码语言:javascript
复制
  17:      }
代码语言:javascript
复制
  18:   
代码语言:javascript
复制
  19:      return hostBuilder
代码语言:javascript
复制
  20:          .ConfigureServices(services =>
代码语言:javascript
复制
  21:          {
代码语言:javascript
复制
  22:              if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
代码语言:javascript
复制
  23:              {
代码语言:javascript
复制
  24:                  services.AddSingleton(typeof(IStartup), startupType);
代码语言:javascript
复制
  25:              }
代码语言:javascript
复制
  26:              else
代码语言:javascript
复制
  27:              {
代码语言:javascript
复制
  28:                  services.AddSingleton(typeof(IStartup), sp =>
代码语言:javascript
复制
  29:                  {
代码语言:javascript
复制
  30:                      var hostingEnvironment = sp.GetRequiredService<IHostEnvironment>();
代码语言:javascript
复制
  31:                      return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName));
代码语言:javascript
复制
  32:                  });
代码语言:javascript
复制
  33:              }
代码语言:javascript
复制
  34:          });
代码语言:javascript
复制
  35:  }
代码语言:javascript
复制
  36:   
代码语言:javascript
复制
  37:  /// <summary>
代码语言:javascript
复制
  38:  /// Specify the startup type to be used by the web host.
代码语言:javascript
复制
  39:  /// </summary>
代码语言:javascript
复制
  40:  /// <param name="hostBuilder">The <see cref="IWebHostBuilder"/> to configure.</param>
代码语言:javascript
复制
  41:  /// <typeparam name ="TStartup">The type containing the startup methods for the application.</typeparam>
代码语言:javascript
复制
  42:  /// <returns>The <see cref="IWebHostBuilder"/>.</returns>
代码语言:javascript
复制
  43:  public static IWebHostBuilder UseStartup<TStartup>(this IWebHostBuilder hostBuilder) where TStartup :class
代码语言:javascript
复制
  44:  {
代码语言:javascript
复制
  45:      return hostBuilder.UseStartup(typeof(TStartup));
代码语言:javascript
复制
  46:  }

创建Startup实例

代码语言:javascript
复制
   1:  /// <summary>
代码语言:javascript
复制
   2:  /// Adds a delegate for configuring additional services for the host or web application. This may be called
代码语言:javascript
复制
   3:  /// multiple times.
代码语言:javascript
复制
   4:  /// </summary>
代码语言:javascript
复制
   5:  /// <param name="configureServices">A delegate for configuring the <see cref="IServiceCollection"/>.</param>
代码语言:javascript
复制
   6:  /// <returns>The <see cref="IWebHostBuilder"/>.</returns>
代码语言:javascript
复制
   7:  public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
代码语言:javascript
复制
   8:  {
代码语言:javascript
复制
   9:      if (configureServices == null)
代码语言:javascript
复制
  10:      {
代码语言:javascript
复制
  11:          throw new ArgumentNullException(nameof(configureServices));
代码语言:javascript
复制
  12:      }
代码语言:javascript
复制
  13:   
代码语言:javascript
复制
  14:      return ConfigureServices((_, services) => configureServices(services));
代码语言:javascript
复制
  15:  }
代码语言:javascript
复制
  16:   
代码语言:javascript
复制
  17:  /// <summary>
代码语言:javascript
复制
  18:  /// Adds a delegate for configuring additional services for the host or web application. This may be called
代码语言:javascript
复制
  19:  /// multiple times.
代码语言:javascript
复制
  20:  /// </summary>
代码语言:javascript
复制
  21:  /// <param name="configureServices">A delegate for configuring the <see cref="IServiceCollection"/>.</param>
代码语言:javascript
复制
  22:  /// <returns>The <see cref="IWebHostBuilder"/>.</returns>
代码语言:javascript
复制
  23:  public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
代码语言:javascript
复制
  24:  {
代码语言:javascript
复制
  25:      _configureServices += configureServices;
代码语言:javascript
复制
  26:      return this;
代码语言:javascript
复制
  27:  }

关于ConfigureServices的定义及注册方式,是在IWebHostBuilder.ConfigureServices实现的,同时可以注意一下25行代码,向大家说明了多次注册Startup的ConfigureServices方法时,会合并起来的根源。此处抽象委托用的也非常多。

该类里面还有Build方法,我就不贴出代码了,只需要知道,主进程在此处开始了。接下来一个比较重要的方法,是BuildCommonServices,它向当前ServiceCollection中添加一些公共框架级服务,以下是部分代码,具体代码请查看WebHostBuilder。

代码语言:javascript
复制
   1:  try
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName);
代码语言:javascript
复制
   4:   
代码语言:javascript
复制
   5:      if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
代码语言:javascript
复制
   6:      {
代码语言:javascript
复制
   7:          services.AddSingleton(typeof(IStartup), startupType);
代码语言:javascript
复制
   8:      }
代码语言:javascript
复制
   9:      else
代码语言:javascript
复制
  10:      {
代码语言:javascript
复制
  11:          services.AddSingleton(typeof(IStartup), sp =>
代码语言:javascript
复制
  12:          {
代码语言:javascript
复制
  13:              var hostingEnvironment = sp.GetRequiredService<IHostEnvironment>();
代码语言:javascript
复制
  14:              var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName);
代码语言:javascript
复制
  15:              return new ConventionBasedStartup(methods);
代码语言:javascript
复制
  16:          });
代码语言:javascript
复制
  17:      }
代码语言:javascript
复制
  18:  }
代码语言:javascript
复制
  19:  catch (Exception ex)
代码语言:javascript
复制
  20:  {
代码语言:javascript
复制
  21:      var capture = ExceptionDispatchInfo.Capture(ex);
代码语言:javascript
复制
  22:      services.AddSingleton<IStartup>(_ =>
代码语言:javascript
复制
  23:      {
代码语言:javascript
复制
  24:          capture.Throw();
代码语言:javascript
复制
  25:          return null;
代码语言:javascript
复制
  26:      });
代码语言:javascript
复制
  27:  }
代码语言:javascript
复制
由此可见,如果我们的Startup类直接实现IStartup,它可以并且将直接注册为IStartup的实现类型。只不过ASP.NET Core模板代码并没有实现IStartup,它更多的是一种约定,并通过DI调用委托,依此调用Startup内的构造函数还有另外两个方法。
代码语言:javascript
复制
同时上述代码还展示了如何创建Startup类型,就是用到了静态方法StartupLoader.LoadMethods类生成StartupMethods实例。

ConfigureServices和Configure

代码语言:javascript
复制
当WebHost初始化时,框架会去查找相应的方法,这里,我们主要查看源代码,其中的核心方法是StartupLoader.FindMethods
代码语言:javascript
复制
   1:  private static MethodInfo FindMethod(Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true)
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName);
代码语言:javascript
复制
   4:      var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, "");
代码语言:javascript
复制
   5:   
代码语言:javascript
复制
   6:      var methods = startupType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
代码语言:javascript
复制
   7:      var selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithEnv, StringComparison.OrdinalIgnoreCase)).ToList();
代码语言:javascript
复制
   8:      if (selectedMethods.Count > 1)
代码语言:javascript
复制
   9:      {
代码语言:javascript
复制
  10:          throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithEnv));
代码语言:javascript
复制
  11:      }
代码语言:javascript
复制
  12:      if (selectedMethods.Count == 0)
代码语言:javascript
复制
  13:      {
代码语言:javascript
复制
  14:          selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithNoEnv, StringComparison.OrdinalIgnoreCase)).ToList();
代码语言:javascript
复制
  15:          if (selectedMethods.Count > 1)
代码语言:javascript
复制
  16:          {
代码语言:javascript
复制
  17:              throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithNoEnv));
代码语言:javascript
复制
  18:          }
代码语言:javascript
复制
  19:      }
代码语言:javascript
复制
  20:   
代码语言:javascript
复制
  21:      var methodInfo = selectedMethods.FirstOrDefault();
代码语言:javascript
复制
  22:      if (methodInfo == null)
代码语言:javascript
复制
  23:      {
代码语言:javascript
复制
  24:          if (required)
代码语言:javascript
复制
  25:          {
代码语言:javascript
复制
  26:              throw new InvalidOperationException(string.Format("A public method named '{0}' or '{1}' could not be found in the '{2}' type.",
代码语言:javascript
复制
  27:                  methodNameWithEnv,
代码语言:javascript
复制
  28:                  methodNameWithNoEnv,
代码语言:javascript
复制
  29:                  startupType.FullName));
代码语言:javascript
复制
  30:   
代码语言:javascript
复制
  31:          }
代码语言:javascript
复制
  32:          return null;
代码语言:javascript
复制
  33:      }
代码语言:javascript
复制
  34:      if (returnType != null && methodInfo.ReturnType != returnType)
代码语言:javascript
复制
  35:      {
代码语言:javascript
复制
  36:          if (required)
代码语言:javascript
复制
  37:          {
代码语言:javascript
复制
  38:              throw new InvalidOperationException(string.Format("The '{0}' method in the type '{1}' must have a return type of '{2}'.",
代码语言:javascript
复制
  39:                  methodInfo.Name,
代码语言:javascript
复制
  40:                  startupType.FullName,
代码语言:javascript
复制
  41:                  returnType.Name));
代码语言:javascript
复制
  42:          }
代码语言:javascript
复制
  43:          return null;
代码语言:javascript
复制
  44:      }
代码语言:javascript
复制
  45:      return methodInfo;
代码语言:javascript
复制
  46:  }
代码语言:javascript
复制
它查找的第一个委托是ConfigureDelegate,该委托将用于构建应用程序的中间件管道。FindMethod完成了大部分工作,具体的代码请查看StartupLoader。此方法根据传递给它的methodName参数在Startup类中查找响应的方法。
代码语言:javascript
复制
我们知道,Startup的定义更多的是约定,所以会去查找Configure和ConfigureServices。当然,通过源代码我还知道,除了提供标准的“Configure”方法之外,我们还可以通过环境配置找到响应的Configure和ConfigureServices。根本来说,我们最终查找到的是ConfigureContainerDelegate。
代码语言:javascript
复制
接下来,一个比较重要的方法是LoadMethods
代码语言:javascript
复制
   1:  public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName)
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      var configureMethod = FindConfigureDelegate(startupType, environmentName);
代码语言:javascript
复制
   4:   
代码语言:javascript
复制
   5:      var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName);
代码语言:javascript
复制
   6:      var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName);
代码语言:javascript
复制
   7:   
代码语言:javascript
复制
   8:      object instance = null;
代码语言:javascript
复制
   9:      if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic))
代码语言:javascript
复制
  10:      {
代码语言:javascript
复制
  11:          instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType);
代码语言:javascript
复制
  12:      }
代码语言:javascript
复制
  13:   
代码语言:javascript
复制
  14:      // The type of the TContainerBuilder. If there is no ConfigureContainer method we can just use object as it's not
代码语言:javascript
复制
  15:      // going to be used for anything.
代码语言:javascript
复制
  16:      var type = configureContainerMethod.MethodInfo != null ? configureContainerMethod.GetContainerType() : typeof(object);
代码语言:javascript
复制
  17:   
代码语言:javascript
复制
  18:      var builder = (ConfigureServicesDelegateBuilder) Activator.CreateInstance(
代码语言:javascript
复制
  19:          typeof(ConfigureServicesDelegateBuilder<>).MakeGenericType(type),
代码语言:javascript
复制
  20:          hostingServiceProvider,
代码语言:javascript
复制
  21:          servicesMethod,
代码语言:javascript
复制
  22:          configureContainerMethod,
代码语言:javascript
复制
  23:          instance);
代码语言:javascript
复制
  24:   
代码语言:javascript
复制
  25:      return new StartupMethods(instance, configureMethod.Build(instance), builder.Build());
代码语言:javascript
复制
  26:  }
代码语言:javascript
复制
代码语言:javascript
复制
该方法通过查找对应的方法,由于Startup并未在DI中注册,所以会调用GetServiceOrCreateInstance创建一个Startup实例,此时构造函数也在此得到解析。
代码语言:javascript
复制
通过一系列的调用,最终到达了ConfigureServicesBuilder.Invoke里面。Invoke方法使用反射来获取和检查在Startup类上定义的ConfigureServices方法所需的参数。
代码语言:javascript
复制
   1:  private IServiceProvider InvokeCore(object instance, IServiceCollection services)
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      if (MethodInfo == null)
代码语言:javascript
复制
   4:      {
代码语言:javascript
复制
   5:          return null;
代码语言:javascript
复制
   6:      }
代码语言:javascript
复制
   7:   
代码语言:javascript
复制
   8:      // Only support IServiceCollection parameters
代码语言:javascript
复制
   9:      var parameters = MethodInfo.GetParameters();
代码语言:javascript
复制
  10:      if (parameters.Length > 1 ||
代码语言:javascript
复制
  11:          parameters.Any(p => p.ParameterType != typeof(IServiceCollection)))
代码语言:javascript
复制
  12:      {
代码语言:javascript
复制
  13:          throw new InvalidOperationException("The ConfigureServices method must either be parameterless or take only one parameter of type IServiceCollection.");
代码语言:javascript
复制
  14:      }
代码语言:javascript
复制
  15:   
代码语言:javascript
复制
  16:      var arguments = new object[MethodInfo.GetParameters().Length];
代码语言:javascript
复制
  17:   
代码语言:javascript
复制
  18:      if (parameters.Length > 0)
代码语言:javascript
复制
  19:      {
代码语言:javascript
复制
  20:          arguments[0] = services;
代码语言:javascript
复制
  21:      }
代码语言:javascript
复制
  22:   
代码语言:javascript
复制
  23:      return MethodInfo.Invoke(instance, arguments) as IServiceProvider;
代码语言:javascript
复制
  24:  }

最后我们来看一下ConfigureBuilder类,它需要一个Action<IApplicationBuilder>委托变量,其中包含每个IStartupFilter的一组包装的Configure方法,最后一个是Startup.Configure方法的委托。此时,所调用的配置链首先命中的是AutoRequestServicesStartupFilter.Configure方法。并将该委托链作为下一个操作,之后会调用ConventionBasedStartup.Configure方法。这将在其本地StartupMethods对象上调用ConfigureDelegate。

代码语言:javascript
复制
   1:  private void Invoke(object instance, IApplicationBuilder builder)
代码语言:javascript
复制
   2:  {
代码语言:javascript
复制
   3:      // Create a scope for Configure, this allows creating scoped dependencies
代码语言:javascript
复制
   4:      // without the hassle of manually creating a scope.
代码语言:javascript
复制
   5:      using (var scope = builder.ApplicationServices.CreateScope())
代码语言:javascript
复制
   6:      {
代码语言:javascript
复制
   7:          var serviceProvider = scope.ServiceProvider;
代码语言:javascript
复制
   8:          var parameterInfos = MethodInfo.GetParameters();
代码语言:javascript
复制
   9:          var parameters = new object[parameterInfos.Length];
代码语言:javascript
复制
  10:          for (var index = 0; index < parameterInfos.Length; index++)
代码语言:javascript
复制
  11:          {
代码语言:javascript
复制
  12:              var parameterInfo = parameterInfos[index];
代码语言:javascript
复制
  13:              if (parameterInfo.ParameterType == typeof(IApplicationBuilder))
代码语言:javascript
复制
  14:              {
代码语言:javascript
复制
  15:                  parameters[index] = builder;
代码语言:javascript
复制
  16:              }
代码语言:javascript
复制
  17:              else
代码语言:javascript
复制
  18:              {
代码语言:javascript
复制
  19:                  try
代码语言:javascript
复制
  20:                  {
代码语言:javascript
复制
  21:                      parameters[index] = serviceProvider.GetRequiredService(parameterInfo.ParameterType);
代码语言:javascript
复制
  22:                  }
代码语言:javascript
复制
  23:                  catch (Exception ex)
代码语言:javascript
复制
  24:                  {
代码语言:javascript
复制
  25:                      throw new Exception(string.Format(
代码语言:javascript
复制
  26:                          "Could not resolve a service of type '{0}' for the parameter '{1}' of method '{2}' on type '{3}'.",
代码语言:javascript
复制
  27:                          parameterInfo.ParameterType.FullName,
代码语言:javascript
复制
  28:                          parameterInfo.Name,
代码语言:javascript
复制
  29:                          MethodInfo.Name,
代码语言:javascript
复制
  30:                          MethodInfo.DeclaringType.FullName), ex);
代码语言:javascript
复制
  31:                  }
代码语言:javascript
复制
  32:              }
代码语言:javascript
复制
  33:          }
代码语言:javascript
复制
  34:          MethodInfo.Invoke(instance, parameters);
代码语言:javascript
复制
  35:      }
代码语言:javascript
复制
  36:  }

Startup.Configure方法会调用ServiceProvider所解析的相应的参数,该方法还可以使用IApplicationBuilder将中间件添加到应用程序管道中。最终的RequestDelegate是从IApplicationBuilder构建并返回的,至此WebHost初始化完成。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet技术平台 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • Startup讨论
    • Starup所承担的角色
      • Startup编写规范
        • ConfigureServices
          • Configure
            • 扩展Startup方法
            • Startup是如何注册和执行的
              • UseStartup源码
                • 创建Startup实例
                  • ConfigureServices和Configure
                  相关产品与服务
                  消息队列 TDMQ
                  消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档