网站开发学哪种语言,项目协同管理软件,网站建设要学哪些软件有哪些,专门做养老院的网站在上一章ASP.NET Core 运行原理解剖[1]:Hosting中#xff0c;我们介绍了 ASP.NET Core 的启动过程#xff0c;主要是对 WebHost 源码的探索。而本文则是对上文的一个补充#xff0c;更加偏向于实战#xff0c;详细的介绍一下我们在实际开发中需要对 Hosting 做一些配置时经…在上一章ASP.NET Core 运行原理解剖[1]:Hosting中我们介绍了 ASP.NET Core 的启动过程主要是对 WebHost 源码的探索。而本文则是对上文的一个补充更加偏向于实战详细的介绍一下我们在实际开发中需要对 Hosting 做一些配置时经常用到的几种方式。 WebHostBuild
WebHostBuild 用来构建 WebHost 也是我们最先接触的一个类它提供了如下方法
ConfigureAppConfiguration
Configuration 在 ASP.NET Core 进行了全新的设计使其更加灵活简洁可以支持多种数据源。在 ASP.NET Core 1.x 中我们是在Startup的构造函数中配置各种数据源的而在 ASP.NET Core 2.0 中则移动了到Program中这样能与控制台应用程序保持一致
public static class WebHostBuilderExtensions{ public static IWebHostBuilder ConfigureAppConfiguration(this IWebHostBuilder hostBuilder, ActionIConfigurationBuilder configureDelegate) { return hostBuilder.ConfigureAppConfiguration((context, builder) configureDelegate(builder));}
} public class WebHostBuilder : IWebHostBuilder{ private ListActionWebHostBuilderContext, IConfigurationBuilder _configureAppConfigurationBuilderDelegates; public IWebHostBuilder ConfigureAppConfiguration(ActionWebHostBuilderContext, IConfigurationBuilder configureDelegate) { if (configureDelegate null){ throw new ArgumentNullException(nameof(configureDelegate));}_configureAppConfigurationBuilderDelegates.Add(configureDelegate); return this;}
}
而_configureAppConfigurationBuilderDelegates委托会在 WebHostBuilder 的Build方法中执行生成 IConfiguration 对象并以单例的形式注册到 DI 系统中 我们可以在Startup以及应用程序的任何地方通过 DI 系统来获取到。
而在 上一章 中也介绍过在CreateDefaultBuilder中会通过该方法来添加appsettinggs.json等基本配置的配置源。
UseSetting
UseSetting 是一个非常重要的方法它用来配置 WebHost 中的 IConfiguration 对象。需要注意与上面ConfigureAppConfiguration的区别 WebHost 中的 Configuration 只限于在 WebHost 使用并且我们不能配置它的数据源它只会读取ASPNETCORE_开头的环境变量
private IConfiguration _config;public WebHostBuilder(){_config new ConfigurationBuilder().AddEnvironmentVariables(prefix: ASPNETCORE_).Build();
}
而我们比较熟悉的当前执行环境也是通过该_config来读取的虽然我们不能配置它的数据源但是它为我们提供了一个UseSetting方法为我们提供了一个设置_config的机会
public string GetSetting(string key){ return _config[key];
}
而我们通过UseSetting设置的变量最终也会以MemoryConfigurationProvider的形式添加到上面介绍的ConfigureAppConfiguration所配置的IConfiguration对象中。
UseStartup
UseStartup 这个我们都比较熟悉它用来显式注册我们的Startup类可以使用泛性Type , 和程序集名称三种方式来注册
// 常用的方法public static IWebHostBuilder UseStartupTStartup(this IWebHostBuilder hostBuilder) where TStartup : class{ return hostBuilder.UseStartup(typeof(TStartup));
}// 通过指定的程序集来注册 Startup 类public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, string startupAssemblyName){ if (startupAssemblyName null){ throw new ArgumentNullException(nameof(startupAssemblyName));} return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName).UseSetting(WebHostDefaults.StartupAssemblyKey, startupAssemblyName);
} // 最终的 Startup 类注册方法上面两种只是一种简写形式public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType){....
}
具体的注册方式在 上一章 也介绍过就是通过反射创建实例然后注入到 DI 系统中。
ConfigureLogging
ConfigureLogging 用来配置日志系统在 ASP.NET Core 1.x 中是在Startup类的Configure方法中通过ILoggerFactory扩展来注册的在 ASP.NET Core 中也变得更加简洁并且统一通过 WebHostBuild 来配置
public static class WebHostBuilderExtensions{ public static IWebHostBuilder ConfigureLogging(this IWebHostBuilder hostBuilder, ActionILoggingBuilder configureLogging) { return hostBuilder.ConfigureServices(collection collection.AddLogging(configureLogging));} public static IWebHostBuilder ConfigureLogging(this IWebHostBuilder hostBuilder, ActionWebHostBuilderContext, ILoggingBuilder configureLogging) { return hostBuilder.ConfigureServices((context, collection) collection.AddLogging(builder configureLogging(context, builder)));}
}
AddLogging 是Microsoft.Extensions.Logging提供的扩展方法更具体的可以看我之前介绍的 ASP.NET Core 源码学习之 Logging 系列。
ConfigureServices
在上面的几个方法中多次用到 ConfigureServices而 ConfigureServices 与 Starup 中的 ConfigureServices 类似都是用来注册服务的:
private readonly ListActionWebHostBuilderContext, IServiceCollection _configureServicesDelegates;public IWebHostBuilder ConfigureServices(ActionWebHostBuilderContext, IServiceCollection configureServices){ if (configureServices null){ throw new ArgumentNullException(nameof(configureServices));}_configureServicesDelegates.Add(configureServices); return this;
}
但不同的是_configureServicesDelegates的执行时机较早是在WebHostBuilder的Build方法中执行的所以会参与 WebHost 中hostingServiceProvider的构建。
其它
WebHostBuild 中还有很多配置的方法就不再一一细说在这里简单介绍一下
UseContentRoot 使用UseSetting方法配置IConfiguration[contentRoot]表示应用程序所在的默认文件夹地址如 MVC 中视图的查询根目录。UseWebRoot 使用UseSetting方法配置IConfiguration[webroot]用来指定可让外部可访问的静态资源路径默认为wwwroot并且是以contentRoot为根目录。CaptureStartupErrors 使用UseSetting方法配置IConfiguration[captureStartupErrors]表示是否捕捉启动时的异常如果为ture则在启动时发生异常也会启动 Http Server并显示错误页面否则不会启动 Http Server。UseEnvironment 使用UseSetting方法配置IConfiguration[environment]用来指定执行环境。UseServer 用来配置 Http Server 服务UseKestrel便是此方法的简写形式。UseUrls 使用UseSetting方法配置IConfiguration[urls]用来配置 Http 服务器地址多个使用;分割。UseShutdownTimeout 使用UseSetting方法配置IConfiguration[shutdownTimeoutSeconds]用来设置 ASP.NET Core 停止时等待的时间。DetailedErrors 表示是否显示详细的错误信息可为true/false或1/0默认为 false但它没有提供直接配置的方法可以通过UseSetting来指定IConfiguration[detailedErrors]。
ISartup
ISartup 是我们比较熟悉的因为在我们创建一个默认的 ASP.NET Core 项目时都会有一个Startup.cs文件包含三个约定的方法按执行顺序排列如下
1. ConfigureServices
ASP.NET Core 框架本身提供了一个 DI依赖注入系统并且可以非常灵活的去扩展很容易的切换成其它的 DI 框架如 AutofacNinject 等。在 ASP.NET Core 中所有的实例都是通过这个 DI 系统来获取的并要求我们的应用程序也使用 DI 系统以便我们能够开发出更具弹性更易维护测试的应用程序。总之在 ASP.NET Core 中一切皆注入。关于 “依赖注入” 这里就不再多说。
在 DI 系统中想要获取服务首先要进行注册而ConfigureServices方法便是用来注册服务的。
public void ConfigureServices(IServiceCollection services){services.AddScopedIUserService, UserService();
}
如上我们为IUserService接口注册了一个UserService类型的实例。
2. ConfigureContainer不常用
ConfigureContainer 是用来替换 DI 框架的如下我们将 ASP.NET Core 内置的 DI 框架替换为 Autofac
public void ConfigureContainer(ContainerBuilder builder){builder.RegisterModule(new AutofacModule());
}
虽然 ASP.NET Core 自带的 DI 系统只提供了构造函数注入以及不支持命名实例等但我喜欢它的简洁并且不太喜欢依赖太多第三库一直也只使用了内置的DI框架因此对这个方法也不太了解就不再多说。
3. Configure
Configure 接收一个IApplicationBuilder类型参数而IApplicationBuilder在 上一章 中介绍过它是用来构建请求管道的因此也可以说 Configure 方法是用来配置请求管道的通常会在这里会注册一些中间件。
public void Configure(IApplicationBuilder app){app.Use(next { return async (context) { await context.Response.WriteAsync(Hello ASP.NET Core!);};});
}
所谓中间件也就是对 HttpContext 进行处理的一种便捷方式下文会详细来介绍。而如上代码我们注册了一个最简单的中间件通过浏览器访问便可以看到 “Hello ASP.NET Core!” 。
通常我们的 Startup 类并没有去实现IStartup接口这是因为我们在Configure方法中大多时候可能需要获取一些其它的服务如我刚才注册的IUserService我们可以直接添加到 Configure 方法的参数列表当中
public void Configure(IApplicationBuilder app, IUserService userService) { }
ASP.NET Core 会通过 DI 系统来解析到 userService 实例但是 ASP.NET Core 中的 DI 系统是不支持普通方法的参数注入的而是手动通过反射的方式来实现的
services.AddSingleton(typeof(IStartup), sp
{ var hostingEnvironment sp.GetRequiredServiceIHostingEnvironment(); var methods StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName); return new ConventionBasedStartup(methods);
});
而通过反射也可以为我们带来更大的灵活性上面的LoadMethods方法会根据当前的执行环境名称来查找适当的方法名
public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName){ var configureMethod FindConfigureDelegate(startupType, environmentName);
} private static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName){ var configureMethod FindMethod(startupType, Configure{0}, environmentName, typeof(void), required: true); return new ConfigureBuilder(configureMethod);
}
更具体的可以查看 StartupLoaderASP.NET Core 会根据当前环境的不同而执行不同的方法
public void ConfigureServices(IServiceCollection services) { }public void ConfigureDevelopmentServices(IServiceCollection services) { }public void ConfigureContainer(ContainerBuilder builder) {}public void ConfigureDevelopmentContainer(ContainerBuilder builder) { }public void Configure(IApplicationBuilder app) { }public void ConfigureDevelopment(IApplicationBuilder app) { }
如上当在Development环境上执行时会选择带Development的方法来执行。
而在默认模版中是通过UseStartupStartup的方式来注册 Startup 类的我们也可以使用上面介绍的指定程序集名称的方式来注册
public static IWebHost BuildWebHost(string[] args) WebHost.CreateDefaultBuilder(args).UseStartup(EmptyWebDemo).Build();
如上我们指定在 EmptyWebDemo 中查找Startup类这样还有一个额外的好处WebHost 同样会根据当前的执行环境来选择不同的Startup类如StartupDevelopment与上面介绍的Startup中方法的查询方式一样。
IHostingStartup
上面我们介绍了Sartup而一个项目中只能一个Sartup因为如果配置多个则最后一个会覆盖之前的。而在一个多层项目中Sartup类一般是放在展现层中我们在其它层也需要注册一些服务或者配置请求管道时通常会写一个扩展方法
public static class EfRepositoryExtensions{ public static void AddEF(this IServiceCollection services,string connectionStringName) { services.AddDbContextAppDbContext(options options.UseSqlServer(connectionStringName), opt opt.EnableRetryOnFailure()));services.TryAddScopedIDbContext, AppDbContext();services.TryAddScoped(typeof(IRepository,), typeof(EfRepository,));...} public static void UseEF(IApplicationBuilder app) {app.UseIdentity();}
}
然后在 Startup 中调用这些扩展方法
public void ConfigureDevelopmentServices(IServiceCollection services){services.AddEF(Configuration.GetConnectionString(DefaultConnection);
}public void ConfigureDevelopment(IApplicationBuilder app){services.UseEF();
}
感觉这种方式非常丑陋而在上一章中我们知道 WebHost 会在 Starup 这前调用 IHostingStartup于是我们便以如下方式来实现
[assembly: HostingStartup(typeof(Zero.EntityFramework.EFRepositoryStartup))]namespace Zero.EntityFramework{ public class EFRepositoryStartup : IHostingStartup{ public void Configure(IWebHostBuilder builder) {builder.ConfigureServices(services {services.AddDbContextAppDbContext(options options.UseSqlServer(connectionStringName), opt opt.EnableRetryOnFailure()));services.TryAddScopedIDbContext, AppDbContext();services.TryAddScoped(typeof(IRepository,), typeof(EfRepository,));...}); builder.Configure(app {app.UseIdentity();});}}
}
如上只需实现 IHostingStartup 接口要清爽简单的多怎一个爽字了得不过还需要进行注册才会被WebHost执行首先要指定HostingStartupAttribute程序集特性其次需要配置 WebHost 中的 IConfiguration[hostingStartupAssemblies]以便 WebHost 能找到我们的程序集可以使用如下方式配置
WebHost.CreateDefaultBuilder(args) // 如需指定多个程序集时使用 ; 分割.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, Zero.Application;Zero.EntityFramework)
这样便完成了 IHostingStartup 注册不过还需要将包含IHostingStartup的程序集放到 Bin 目录下否则根本无法加载。不过 ASP.NET Core 也提供了类似插件的方式来指定IHostingStartup程序集的查找位置可通过设置DOTNET_ADDITIONAL_DEPS和ASPNETCORE_HOSTINGSTARTUPASSEMBLIES来实现而这里就不再多说。
IHostingStartup 是由 WebHostBuilder 来调用的执行时机较早在创建 WebHost 之前执行因此可以替换一些在 WebHost 中需要使用的服务。
IStartupFilter
IStartupFilter 是除Startup和HostingStartup之处另一种配置IApplicationBuilder的方式
public interface IStartupFilter{ ActionIApplicationBuilder Configure(ActionIApplicationBuilder next);
}
它只有一个Configure方法是对 Starup 类中Configure方法的拦截器给我们一个在Configure方法执行之前进行一些配置的机会。
让我们实践一把先定义2个 StartupFilter
public class A : IStartupFilter{ public ActionIApplicationBuilder Configure(ActionIApplicationBuilder next) {Console.WriteLine(This is A1!); return app {Console.WriteLine(This is A2!);next(app);};}
} public class B : IStartupFilter{ public ActionIApplicationBuilder Configure(ActionIApplicationBuilder next) {Console.WriteLine(This is B1!); return app {Console.WriteLine(This is B2!);next(app);};}
}
然后让他们注册到DI系统中WebHost 在执行 Starup 类中Configure方法之前会从 DI 系统中获取所有的IStartupFilter来执行
public void ConfigureServices(IServiceCollection services){services.AddSingletonIStartupFilter, A();services.AddSingletonIStartupFilter, B();
}public void Configure(IApplicationBuilder app){Console.WriteLine(This is Configure!);app.Use(next { return async (context) { await context.Response.WriteAsync(Hello ASP.NET Core!);};});
}
最终它他的执行顺序为B1 - A1 - A2 - B2 - Configure 。
IHostedService
当我们希望随着 ASP.NET Core 的启动来执行一些后台任务如定期的刷新缓存等时并在 ASP.NET Core 停止时可以优雅的关闭则可以使用IHostedService它有如下定义
public interface IHostedService{ Task StartAsync(CancellationToken cancellationToken); Task StopAsync(CancellationToken cancellationToken);
}
很简单只有开始和停止两个方法它的用法大概是这个样子的
public class CacheHostService : IHostedService{ private readonly ICacheService _cacheService; private CancellationTokenSource _cts; private Task _executingTask; public CacheHostService(ICacheService cacheService) {_cacheService cacheService;} public Task StartAsync(CancellationToken cancellationToken) {_cts CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);_executingTask Task.Run(async () { while (!_cts.IsCancellationRequested){Console.WriteLine(cancellationToken: _cts.IsCancellationRequested); await _cacheService.Refresh(); await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);}}); return Task.CompletedTask;} public async Task StopAsync(CancellationToken cancellationToken) { // 发送停止信号以通知我们的后台服务结束执行。_cts.Cancel(); // 等待后台服务的停止而 ASP.NET Core 大约会等待5秒钟可在上面介绍的UseShutdownTimeout方法中配置如果还没有执行完会发送取消信号以防止无限的等待下去。await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));cancellationToken.ThrowIfCancellationRequested();}
}
如上我们定义了一个在台后每5秒刷新一次缓存的服务并在 ASP.NET Core 程序停止时优雅的关闭。最后将它注册到 DI 系统中即可
public void ConfigureServices(IServiceCollection services){services.AddSingletonICacheService, CacheService();services.AddSingletonIHostedService, CacheHostService();
}
WebHost 在启动 HTTP Server 之后会从 DI 系统中获取所有的IHostedService来启动我们注册的 HostedService参见上一章 。
IApplicationLifetime
IApplicationLifetime用来实现 ASP.NET Core 的生命周期钩子我们可以在 ASP.NET Core 停止时做一些优雅的操作如资源的清理等。它有如下定义
public interface IApplicationLifetime{CancellationToken ApplicationStarted { get; }CancellationToken ApplicationStopping { get; }CancellationToken ApplicationStopped { get; } void StopApplication();
}
IApplicationLifetime已被 ASP.NET Core 注册到 DI 系统中我们使用的时候只需要注入即可。它有三个CancellationToken类型的属性是异步方法终止执行的信号表示 ASP.NET Core 生命周期的三个阶段启动开始停止已停止。
public void Configure(IApplicationBuilder app, IApplicationLifetime appLifetime){appLifetime.ApplicationStarted.Register(() Console.WriteLine(Started));appLifetime.ApplicationStopping.Register(() Console.WriteLine(Stopping));appLifetime.ApplicationStopped.Register(() {Console.WriteLine(Stopped);Console.ReadKey();});app.Use(next { return async (context) { await context.Response.WriteAsync(Hello ASP.NET Core!);appLifetime.StopApplication();};});
}
执行结果如下 在上一章中我们提到过 IApplicationLifetime 的启动信号是在 WebHost 的StartAsync方法中触发的而没有提到停止信号的触发在这里补充一下
internal class WebHost : IWebHost{ public async Task StopAsync(CancellationToken cancellationToken default(CancellationToken)) {.... // 设置 Task 的超时时间上文在 IHostedService 中提到过var timeoutToken new CancellationTokenSource(Options.ShutdownTimeout).Token; if (!cancellationToken.CanBeCanceled){cancellationToken timeoutToken;} else{cancellationToken CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken).Token;} // 触发 Stopping 信号_applicationLifetime?.StopApplication(); // 停止 Http Serverif (Server ! null){ await Server.StopAsync(cancellationToken).ConfigureAwait(false);} // 停止 我们注册的 IHostServiceif (_hostedServiceExecutor ! null){ await _hostedServiceExecutor.StopAsync(cancellationToken).ConfigureAwait(false);} // 发送 Stopped 通知_applicationLifetime?.NotifyStopped();}
}
总结
本文详细介绍了对 WebHost 的配置结合 上一章对 ASP.NET Core 的启动流程也基本清楚了下一章就来介绍一下请求管道的创建敬请期待
参考资料
ASP-NET-Core-2-IHostedServiceASPNET-Core-2.0-Stripping-Away-Cross-Cutting-ConcernsLooking-at-asp-net-cores-iapplicationlifetime 相关文章
.NET Core 2.0 正式发布信息汇总.NET Standard 2.0 特性介绍和使用指南.NET Core 2.0 的dll实时更新、https、依赖包变更问题及解决.NET Core 2.0 特性介绍和使用指南Entity Framework Core 2.0 新特性体验 PHP under .NET Core.NET Core 2.0使用NLog升级项目到.NET Core 2.0在Linux上安装Docker并成功部署解决Visual Studio For Mac Restore失败的问题ASP.NET Core 2.0 特性介绍和使用指南.Net Core下通过Proxy 模式 使用 WCF.NET Core 2.0 开源Office组件 NPOIASP.NET Core Razor页面 vs MVCRazor Page–Asp.Net Core 2.0新功能 Razor Page介绍MySql 使用 EF Core 2.0 CodeFirst、DbFirst、数据库迁移Migration介绍及示例.NET Core 2.0迁移技巧之web.config配置文件asp.net core MVC 过滤器之ExceptionFilter过滤器(一)ASP.NET Core 使用Cookie验证身份ASP.NET Core MVC – Tag Helpers 介绍ASP.NET Core MVC – Caching Tag HelpersASP.NET Core MVC – Form Tag HelpersASP.NET Core MVC – 自定义 Tag HelpersASP.NET Core MVC – Tag Helper 组件ASP.NET Core 运行原理解剖[1]:Hosting
原文地址http://www.cnblogs.com/RainingNight/p/hosting-configure-in-asp-net-core.html .NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注