在上一章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, Action<IConfigurationBuilder> configureDelegate)    {         return hostBuilder.ConfigureAppConfiguration((context, builder) => configureDelegate(builder));}
}     public class WebHostBuilder : IWebHostBuilder{         private List<Action<WebHostBuilderContext, IConfigurationBuilder>> _configureAppConfigurationBuilderDelegates;           public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, 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 UseStartup<TStartup>(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, Action<ILoggingBuilder> configureLogging)    {         return hostBuilder.ConfigureServices(collection => collection.AddLogging(configureLogging));}  

     public static IWebHostBuilder ConfigureLogging(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, 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 List<Action<WebHostBuilderContext, IServiceCollection>> _configureServicesDelegates;

public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, 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/false1/0,默认为 false,但它没有提供直接配置的方法,可以通过UseSetting来指定IConfiguration["detailedErrors"]

ISartup

ISartup 是我们比较熟悉的,因为在我们创建一个默认的 ASP.NET Core 项目时,都会有一个Startup.cs文件,包含三个约定的方法,按执行顺序排列如下:

1. ConfigureServices

ASP.NET Core 框架本身提供了一个 DI(依赖注入)系统,并且可以非常灵活的去扩展,很容易的切换成其它的 DI 框架(如 Autofac,Ninject 等)。在 ASP.NET Core 中,所有的实例都是通过这个 DI 系统来获取的,并要求我们的应用程序也使用 DI 系统,以便我们能够开发出更具弹性,更易维护,测试的应用程序。总之在 ASP.NET Core 中,一切皆注入。关于 “依赖注入” 这里就不再多说。

在 DI 系统中,想要获取服务,首先要进行注册,而ConfigureServices方法便是用来注册服务的。

public void ConfigureServices(IServiceCollection services){services.AddScoped<IUserService, 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.GetRequiredService<IHostingEnvironment>();    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);
}

更具体的可以查看 StartupLoader,ASP.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的方法来执行。

而在默认模版中是通过UseStartup<Startup>的方式来注册 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.AddDbContext<AppDbContext>(options =>options.UseSqlServer(connectionStringName), opt => opt.EnableRetryOnFailure()));services.TryAddScoped<IDbContext, 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.AddDbContext<AppDbContext>(options =>options.UseSqlServer(connectionStringName), opt => opt.EnableRetryOnFailure()));services.TryAddScoped<IDbContext, 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_DEPSASPNETCORE_HOSTINGSTARTUPASSEMBLIES来实现,而这里就不再多说。

IHostingStartup 是由 WebHostBuilder 来调用的,执行时机较早,在创建 WebHost 之前执行,因此可以替换一些在 WebHost 中需要使用的服务。

IStartupFilter

IStartupFilter 是除StartupHostingStartup之处另一种配置IApplicationBuilder的方式:

public interface IStartupFilter{  

  Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
}

它只有一个Configure方法,是对 Starup 类中Configure方法的拦截器,给我们一个在Configure方法执行之前进行一些配置的机会。

让我们实践一把,先定义2个 StartupFilter:

public class A : IStartupFilter{   

 public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)    {Console.WriteLine("This is A1!");         return app =>{Console.WriteLine("This is A2!");next(app);};}
}

   public class B : IStartupFilter{   

    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> 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.AddSingleton<IStartupFilter, A>();services.AddSingleton<IStartupFilter, 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.AddSingleton<ICacheService, CacheService>();services.AddSingleton<IHostedService, 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-IHostedService

  • ASPNET-Core-2.0-Stripping-Away-Cross-Cutting-Concerns

  • Looking-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组件 NPOI

  • ASP.NET Core Razor页面 vs MVC

  • Razor 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 Helpers

  • ASP.NET Core MVC – Form Tag Helpers

  • ASP.NET Core MVC – 自定义 Tag Helpers

  • ASP.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跨平台或扫描二维码关注

ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍相关推荐

  1. ASP.NET Core 运行原理解剖[1]:Hosting

    SP.NET Core 是新一代的 ASP.NET,第一次出现时代号为 ASP.NET vNext,后来命名为ASP.NET 5,随着它的完善与成熟,最终命名为 ASP.NET Core,表明它不是 ...

  2. ASP.NET Core 运行原理解剖[5]:Authentication

    在现代应用程序中,认证已不再是简单的将用户凭证保存在浏览器中,而要适应多种场景,如App,WebAPI,第三方登录等等.在 ASP.NET 4.x 时代的Windows认证和Forms认证已无法满足现 ...

  3. ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界

    本系列文章从源码分析的角度来探索 ASP.NET Core 的运行原理,分为以下几个章节: ASP.NET Core 运行原理解剖[1]:Hosting ASP.NET Core 运行原理解剖[2]: ...

  4. ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

    在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展.通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监 ...

  5. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    在上一节(ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行)中提到ASP.NET Core WebApp 必须含有Startup类,在本节中将重点讲解Startup类以及Midd ...

  6. ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行

    之前两篇文章简析.NET Core 以及与 .NET Framework的关系和.NET Core的构成体系从总体上介绍.NET Core,接下来计划用一个系列对ASP.NET Core的运行原理进行 ...

  7. ASP.NET Core 运行原理剖析

    1.1. 概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管.以初始化过程中触发HttpAppl ...

  8. ASP.NET Core 运行原理

    1.1. 概述 在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载.Web应用程序的入口点由InetMgr.exe创建并调用托管.以初始化过程中触发HttpAppl ...

  9. [转]ASP.NET Core 指定环境发布(hosting environment)

    本文转自:https://www.cnblogs.com/xishuai/p/asp-net-core-set-hosting-environment-with-publish.html ASP.NE ...

最新文章

  1. python 将数组中部分转入新数组_python – 如何将掩码从一个数组应用到另一个数组?...
  2. 人人都来写算法 之 快速排序
  3. python 如何中断程序、停止程序、退出程序?sys.exit()、os._exit()、os.kill()
  4. 操作系统实验报告2:Linux 下 x86 汇编语言1
  5. 【Kafka】Kafka 2.6新功能:消费者主动触发Rebalance
  6. 汉字的动态编码与显示方案
  7. Java异常统一处理
  8. redis系列--深入哨兵集群
  9. MP4文件中提取H264码流保存成H264文件
  10. Android-入门教程-内部存储/外部存储-概览
  11. Windows二进制文件合并工具
  12. oracle筛选后怎样重新排序,excel表格筛选后怎么重新排序
  13. 千锋云计算毕业设计论文:论文设计任务书
  14. 护士人文修养测试题答案
  15. CentOS5u11 Oracle 10g 生产RMAN备份可用性恢复测试
  16. BPMN2.0协议解析
  17. 电商核心业务功能测试分析
  18. 电大计算机基础期末考试题库,中央电大计算机基础考试题库大全
  19. 克鲁斯卡尔算法与普里姆算法详解
  20. TensorFlow基础之模型建立与训练:线性回归、MLP多层感知机、卷积神经网络

热门文章

  1. yum安装最新的 LNMP
  2. js遮罩层以及移动端的上拉框
  3. 在Heroku上部署(托管)Rails项目
  4. 嵌入式linux学习步骤(三)
  5. 借用虚拟机在真机安装 ubuntu, 亦可安装至移动硬盘
  6. 回到地球之后,这个男人创建了Ubuntu
  7. 如何使用 C# 判断一个文件是否为程序集
  8. 讲一讲应用服务的新鲜事儿
  9. 里程碑!中文版.NET官网发布,.NET开发起飞!
  10. WPF 如何将IconFont图标转成Geometry