apmserv网站模板,深圳网络营销怎么推广,第三方小程序开发平台有哪些,山西省建设厅招标网站这一篇是接着前一篇在写的。如果没有看过前一篇文章#xff0c;建议先去看一下前一篇#xff0c;这儿是传送门一、前言前一篇文章#xff0c;我们从应用启动时异步运行任务开始#xff0c;说到了必要性#xff0c;也说到了几种解决方法#xff0c;及各自的优缺点。最后建议先去看一下前一篇这儿是传送门 一、前言前一篇文章我们从应用启动时异步运行任务开始说到了必要性也说到了几种解决方法及各自的优缺点。最后还提出了一个比较合理的解决方法通过在Program.cs里加入代码来实现IWebHost启动前运行异步任务。实现的代码再贴一下public class Program
{public static async Task Main(string[] args){IWebHost webHost CreateWebHostBuilder(args).Build();using (var scope webHost.Services.CreateScope()){var myDbContext scope.ServiceProvider.GetRequiredServiceMyDbContext();await myDbContext.Database.MigrateAsync();}await webHost.RunAsync();}public static IWebHostBuilder CreateWebHostBuilder(string[] args) WebHost.CreateDefaultBuilder(args).UseStartupStartup();
}
这个方法是有效的。但是也会有一点不足。从.Net Core的最简规则来说我们不应该在Program.cs中加入其它代码。当然我们可以把这部分代码转到一个外部类中但最后也必须手动加入到Program.cs中。尤其是在多个应用中使用相同的模式时这种方式会很麻烦。也许我们可以采用向DI容器中注入启动任务二、向DI容器中注入启动任务这种方式是基于IStartupFilter和IHostedService两个接口通过这两个接口可以向依赖注入容器中注册类。 首先我们为启动任务创建一个简单接口public interface IStartupTask
{Task ExecuteAsync(CancellationToken cancellationToken default);
}
再建一个扩展方法用来向DI容器注册启动任务public static class ServiceCollectionExtensions
{public static IServiceCollection AddStartupTaskT(this IServiceCollection services)where T : class, IStartupTask services.AddTransientIStartupTask, T();
}
最后再建一个扩展方法在应用启动时查找所有已注册的IStartupTask按顺序执行他们然后启动IWebHostpublic static class StartupTaskWebHostExtensions
{public static async Task RunWithTasksAsync(this IHost webHost, CancellationToken cancellationToken default){var startupTasks webHost.Services.GetServicesIStartupTask();foreach (var startupTask in startupTasks){await startupTask.ExecuteAsync(cancellationToken);}await webHost.RunAsync(cancellationToken);}
}
这样就齐活了。 还是用一个例子来看看这个方式的具体应用。三、示例 - 数据迁移实现IStartupTask其实和实现IStartupFilter很相似可以从DI容器中注入。如果需要考虑作用域还可以注入IServiceProvider并手动创建作用域。 例子中数据迁移类可以写成这样public class MigratorStartupFilter: IStartupTask
{private readonly IServiceProvider _serviceProvider;public MigratorStartupFilter(IServiceProvider serviceProvider){_serviceProvider serviceProvider;}public async Task ExecuteAsync(CancellationToken cancellationToken default){using(var scope _seviceProvider.CreateScope()){var myDbContext scope.ServiceProvider.GetRequiredServiceMyDbContext();await myDbContext.Database.MigrateAsync();}}
}
下面把任务注入到ConfigureServices()中public void ConfigureServices(IServiceCollection services)
{services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);services.AddStartupTaskMigrationStartupTask();
}
最后用上一节中的扩展方法RunWithTasksAsync()来替代Program.cs中的Run():public class Program
{public static async Task Main(string[] args){// await CreateWebHostBuilder(args).Build().RunAsync();await CreateWebHostBuilder(args).Build().RunWithTasksAsync();}public static IWebHostBuilder CreateWebHostBuilder(string[] args) WebHost.CreateDefaultBuilder(args).UseStartupStartup();
}从功能上来说跟上一篇的代码区别不大但这样的写法又多了一些优点任务代码放到了Program.cs之外。这符合微软的建议也更容易理解任务放到了DI容器中这样更容易添加额外的任务如果没有额外任务这个代码和标准的Run()一样所以这个代码可以独立成一个模板。简单来说使用RunWithTasksAsync()后可以轻松地向DI容器添加额外的任务而不需要任何其它的更改。 满意了吗好像感觉还差一点点…四、不够完美的地方如果要照着完美去做好像还差一点点。这个一点点是在于任务现在运行在IConfiguration和DI容器配置完成后IStartupFilters运行和中间件管道配置完成之前。换句话说如果任务需要依赖于IStartupFilters那这个方案行不通。在大多数情况下这没什么问题。以我自己的经验来看好像没有什么功能需要依赖于IStartupFilters。但作为一个框架类的代码需要考虑这种情况发生的可能性。以目前的方案来说好像还没办法解决。应用启动时当调用WebHost.Run()时是内部调用WebHost。看一下StartAsync()的简化代码public virtual async Task StartAsync(CancellationToken cancellationToken default)
{_logger _applicationServices.GetRequiredServiceILoggerWebHost();var application BuildApplication();_applicationLifetime _applicationServices.GetRequiredServiceIApplicationLifetime() as ApplicationLifetime;_hostedServiceExecutor _applicationServices.GetRequiredServiceHostedServiceExecutor();var diagnosticSource _applicationServices.GetRequiredServiceDiagnosticListener();var httpContextFactory _applicationServices.GetRequiredServiceIHttpContextFactory();var hostingApp new HostingApplication(application, _logger, diagnosticSource, httpContextFactory);await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false);_applicationLifetime?.NotifyStarted();await _hostedServiceExecutor.StartAsync(cancellationToken).ConfigureAwait(false);
}
如果我们希望任务是加在BuildApplication()调用和Server.StartAsync()的调用之间该怎么办这段代码能给出答案我们需要装饰IServer。¨K16K 首先我们替换IServer的实现¨G8G 在这段代码中我们拦截StartAsync()调用并注入任务然后回到内置处理。下面是对应的扩展代码¨G9G 这个扩展代码做了两件事在DI容器中注册了IStartupTask并装饰了之前注册的IServer实例。装饰方法Decorate()我略过了有兴趣的可以去了解一下 - 装饰模式。 Program.cs的代码和第三节的代码相同略过。emsp; 我们终于做到了在应用程序完全构建完成后去执行我们的任务包括IStartupFilters和中间件管道。现在的流程类似于下面这个微软官方的图全文完