Exploring Program.cs, Startup.cs and CreateDefaultBuilder in ASP.NET Core 2 preview 1

ASP.NET Core 2.0 的目标之一是已经被简洁化的基础模板。简化了其基本使用,并且让开始一个新项目变得更加简单。

明显从表面上来看,新的 ProgramStartup 类型相比于 ASP.NET Core 1.0 更加简单。现在,我将从新的 WebHost.CreateDefaultBuilder() 方法出发,看看它是如何引导你的应用程序的。

Program和Startup在ASP.NET Core 1.X中的职责


在ASP.NET Core 1.X中, Program类用来建立IWebHost。一个基础的web app模板就像这样:

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        host.Run();
    }
}

这个相对紧凑的文件做了下面这些事情:

  • 配置一个Web服务器(Kestrel)
  • 设置内容目录(这个目录包括appsettings.json文件等)
  • 建立IIS集
  • 定义Startup类
  • Build()和Run IWebHost

Startup的实现可能存在很大的差别,这取决于你建立的应用程序。 MVC模板展现的是一个典型的启动模板

public class Startup
{
    public Startup(IHostingEnviroment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsetting.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        // add framework services
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if(env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{Controller=Home}/{action=Index}/{id?}"
            );
        });
    }
}

这个文件主要有以下四个职责:

  • 在Startup构造函数中建立配置
  • 在ConfigureService中建立依赖注入关系
  • 在Configure中启动Logging
  • 在Configure中建立中间件管道

这些都运行的不错,但是ASP.NET团队认为还是有很多地方考虑的不够全面。

首先,设置配置比较冗长,但也很标准,总的来说不太需要过多的调整。

其次,Logging是在Startup的Configure中配置的,它建立在Configuration和DI配置完成之后。这里有两个缺点。一方面,这使Logging看上去像个二等公民-Configure通常被用来配置中间件管道,这样使得Logging配置在这里意义不大。同样也意味着你无法记录应用程序本身引导过程的日志。有很多方法能解决这个问题,但都不是特别有效。

在ASP.NET Core 2.0 preview 1中,这两点已经通过修改IWebHost角色,通过创建一个辅助方法配置应用程序。

Program和Startup在ASP.NET Core 2.0 preview 1中的责任


在ASP.NET Core 2.0 previe 1中,IWebHost的职责发生了一些改变。在之前拥有的职责上,增加了额外两点:

  • Setup Configuration
  • Setup Logging

此外,ASP.NET Core 2.0引入了一个辅助方法CreateDefaultBuilder。它封装了Program.cs中大部分常见的代码,以及configuration和logging!

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}

正如你所看到的,这里没有提及Kestrel,IIS,configuration等-这些全部放在CreateDefaultBuilder方法中处理。

将configuration和logging代码转移到这个方法中处理简化了Startup文件:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if(env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}"
            );
        });
    }
}

这个类除了logging和许多configuration代码被移除外,和1.0的非常相似。注意IConfiguration对象注入到类型的一个属性,而不是创建配置构造函数本身。

这是ASP.NET 2.0新引入的-IConfiguration对象默认通过DI注册。在1.X中,如果你需要从DI中获取IConfiguration对象,你必须自己注册。

最初,我认为CreateDefaultBuilder只是进行了模糊的设置,感觉好像有点退步,但是回想起来,这里更多的只是一个“谁动了我的奶酪”的感觉。CreateDefaultBuilder没有什么奇特的地方,他只是隐藏了一些通常保持不变的标准代码。

WebHost.CreateDefaultBuilder辅助方法


为了能确切地弄懂静态辅助方法CreateDefaultBuilder方法,我决定看一看GitHub上的源代码! 你会惊喜地发现,如果你使用过ASP.NET Core 1.X, 这些看起来都很相似。

public static IWebHostBuilder CreateDefaultBulder(string[] args)
{
    var builder = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .ConfigureAppConfiguration((hostingContext, config) => {/* setup config */})
        .ConfigureLogging((hostingContext, config) => {/* setup logging */})
        .UseIISIntegration()
        .UseDefaultServiceProvider((context, options) => {/* setup the DI container to use */})
        .ConfigureServices(services =>
        {
            services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
        });

    return builder;
}

这里一些新的方法被我省略了,我将在以后的帖子对这些方法进行解析。你能看出来这些方法和ASP.NET Core 1.0中很大程度上做着相同的工作-建立Kestrel服务器,定义ContentRoot,建立IIS集。另外,它也做了一些其他的事情

  • ConfigureAppConfiguration-这里包含过去编写在Startup的配置代码
  • ConfigureLogging-建立过去在Startup.Configure中的logging配置
  • UseDefaultServiceProvider-后面我会就此深入讨论, 但是这里设置内置的DI容器,并且允许你定制自定义行为
  • ConfigureServices-向IWebHost中添加额外所需的组件服务。特别地,可以配置Kestrel服务的选项,允许你可以轻松定义你的Web主机就像普通配置的一部分一样。

这篇帖子中,我将仔细看看configuration和logging,其他方法的深入将在以后的帖子中展现。

在ConfigureAppConfiguration中设置app configuration


ConfigureAppConfiguration方法中是一个包含两个参数的lambda表达式-WebHostBuilderContext对象hostingContext,和IConfigurationBuilder实例config:

ConfigureAppConfiguration((hostingContext, config) =>
{
    var env = hostingContext.HostingEnvironment;

    config.AddJsonFile("appsetting.json, optional:true, reloadOnChange: true")
        .AddJsonFile($"appsetting.{env.Environment}.json", optional: true, reloadOnChange: true);

    if(env.IsDevelopment())
    {
        var appAssembly = Assembly.Load(new Assembly(env.ApplicationName));
        if(appAssembly != null)
        {
            config.AddUserSecrets(appAssembly, optional: true);
        }
    }

    config.AddEnvironmentVariables();

    if(args != null)
    {
        config.AddCommandLine(args);
    }
});

可以看到,参数hostingContext暴露了IHostingEnvironment(不管是运行在开发环境还是生产环境)作为一个属性,HostingEnvironment。如果你使用过ASP.NET Core 2.0, 除了形式上意外,大部分代码都非常相似。

一个特例是在建立User Secret时候,做法有一点点不同。它通过一个程序集引用加载User Secret,但你仍然可以使用通用config.AddUserSecrets<T>进行配置。

在ASP.NET Core 2.0中, UserSecretsId被存储在一个Assembly Attribute中,因此需要先引入Assembly中。你仍然可以使用csproj文件定义id-它将会在编译时被嵌入到assembly level attribute中。

这些都是标准的东西。它通过下面的提供者,通过下面的顺序加载配置:

  • appsettings.json(optional)
  • appsettings.{env.EnvironmentName}.json(optional)
  • User Secrets
  • Environment Variables
  • Command line arguments

这个方法与ASP.NET Core 1.X的主要不同在于所处的位置-config现在作为WebHost的一部分,而不是通过构造函数植入。同时,最初的建立和最终的调用通过Web Host本身的IConfigurationBuilder,而不是你处理。

在ConfigureLogging中配置logging


ConfigureLogging方法同样是两个参数的lambda表达式-一个WebHostBuilderContext对象hostingContext,一个和configuration方法一样的LoggerFactory实例logging:

ConfigureLogging((hostingContext, logging) =>
{
    logging.UseConfiguration(hostingContext.Configuration.GetSection("Logging"));
    logging.AddConsole();
    logging.AddDebug();
});

logging基础设施在ASP.NET Core 2.0中有一些改变。但是一般来说,这些代码与你在ASP.NET Core1.0应用程序的Configure方法中发现的建立Console和Debug日志方式遥相呼应。你可以用UseConfiguration方法设置日志级别,通过访问已定义好的暴露在hostingContext.Configuration的IConfiguration接口。

自定义WebHostBuilder


我希望通过对WebHost.CreateDefaultBuilder的深入探讨能够表达ASP.NET团队之所以使用它的原因。有许多方法可以建立并运行一个app,但是这种方式使得它更加简单。

但是,假如这不是你想要的启动方式?你可以不适用它!没有什么特殊的辅助,你可以复制粘贴实现代码到你自己的app中,定制它,同样也可以很好的工作。

这不是很确切-KestrelServerOptionsSetup类在ConfigureServices内部引用,因此你将它删除。我将在后面的帖子中进行深入分析。

总结

这篇帖子看起来像是介绍从ASP.NET Core 1.X向ASP.NET Core 2.0 preview 1转移中Program.cs和Startup.cs的不同。特别地,我稍微深入了解了一下WebHost.CreateDefaultBuilder方法,它的作用实际上只是使建立app更加简单。如果你不喜欢他给你做的选择,或者需要自己定制。你仍然可以做到这点,就像以前一样。选择在于你自己!


ASP.NET Core 2 preview 1中Program.cs,Startup.cs和CreateDefaultBuilder的探索的更多相关文章

  1. asp.net core 3.1 入口:Program.cs中的Main函数

    本文分析Program.cs 中Main()函数中代码的运行顺序分析asp.net core程序的启动,重点不是剖析源码,而是理清程序开始时执行的顺序.到底用了哪些实例,哪些法方. asp.net c ...

  2. 从ASP.Net Core Web Api模板中移除MVC Razor依赖项

    前言 :本篇文章,我将会介绍如何在不包括MVC / Razor功能和包的情况下,添加最少的依赖项到ASP.NET Core Web API项目中. 一.MVC   VS WebApi (1)在ASP. ...

  3. 跨平台应用集成(在ASP.NET Core MVC 应用程序中集成 Microsoft Graph)

    作者:陈希章 发表于 2017年6月25日 谈一谈.NET 的跨平台 终于要写到这一篇了.跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标.我在前面的文章已经提到过了,Micro ...

  4. Asp.Net Core 入门(一)——Program.cs做了什么

    ASP.NET Core 是微软推出的一种全新的跨平台开源 .NET 框架,用于在 Windows.Mac 或 Linux 上生成基于云的新式 Web 应用程序.国内目前关于Asp.Net Core的 ...

  5. ASP.NET Core 3.0 WebApi中使用Swagger生成API文档简介

    参考地址,官网:https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/getting-started-with-swashbuckle?view ...

  6. Asp.Net Core 第02局:Program

    总目录 前言 本文介绍Program,它包含程序的入口Main方法.从这里开始... 环境 1.Visual Studio 2017 2.Asp.Net Core 2.2 开局 第一手:Program ...

  7. 在 ASP.NET Core和Worker Service中使用Quartz.Net

    现在有了一个官方包Quartz.Extensions.Hosting实现使用Quartz.Net运行后台任务,所以把Quartz.Net添加到ASP.NET Core或Worker Service要简 ...

  8. ASP.NET Core 在 JSON 文件中配置依赖注入

    前言 在上一篇文章中写了如何在MVC中配置全局路由前缀,今天给大家介绍一下如何在在 json 文件中配置依赖注入. 在以前的 ASP.NET 4+ (MVC,Web Api,Owin,SingalR等 ...

  9. VS 2017开发ASP.NET Core Web应用过程中发现的一个重大Bug

    今天试着用VS 2017去开发一个.net core项目,想着看看.net core的开发和MVC5开发有什么区别,然后从中发现了一个VS2017的Bug. 首先,我们新建项目,ASP.NET Cor ...

随机推荐

  1. Win10 连接CentOS 8 的Docker容器中 SqlServer数据库

    楔子 工作在win10环境下,使用Docker Windows桌面版容器化SqlServer数据库连接使用(主要是想用Docker),但是同时需要Linux系统测试,win10 下VMware 虚拟机 ...

  2. 利用Python迭代器查找最小值和最大值

    迭代器的用法为for...in.... 迭代器如同for循环,可以遍历所有的值,但我们熟悉的的语言,都是通过下标完成的,python的循环程度要高于C语言的循环,因为python的迭代不止可以用在Li ...

  3. 利用AXI VDMA实现OV5640摄像头采集笔记(二)

    导读:摄像头采样图像数据后经过VDMA进入DDR,通过PS部分控制,经过三级缓存,将DDR中保持的图形数据通过VDMA发送出去.在FPGA的接收端口产生VID OUT时序驱动HDMI显示器显示图形. ...

  4. 短期Flag

    十一我不想放假,我想成为那10个被拉出去去南方虐的人之一. 然而我现在,最近考得连能不能稳在之留下10个人的第一机房都有点悬. 最近的问题都无法解决,这个短期flag我也不想让它倒啊! 所以,Deep ...

  5. js取两位小数点

    var money = one.money; var tmoney = money.substr(0,money.indexOf(".")+3);

  6. 「BZOJ1576」[Usaco2009 Jan] 安全路经Travel------------------------P2934 [USACO09JAN]安全出行Safe Travel

    原题地址 题目描述 Gremlins have infested the farm. These nasty, ugly fairy-like creatures thwart the cows as ...

  7. python中字符串常见操作(二)

    # 可迭代对象有:字典,列表,元组,字符串,集合 str1 = '192.168.1.1' str2 = 'as df gh jk' str3 = '小李子' str4 = ['aa','bb','c ...

  8. 013.Kubernetes二进制部署worker节点Nginx实现高可用

    一 Nginx代理实现kube-apiserver高可用 1.1 Nginx实现高可用 基于 nginx 代理的 kube-apiserver 高可用方案. 控制节点的 kube-controller ...

  9. 《Java多线程面试题》系列-创建线程的三种方法及其区别

    1. 创建线程的三种方法及其区别 1.1 继承Thread类 首先,定义Thread类的子类并重写run()方法: package com.zwwhnly.springbootaction.javab ...

  10. Unity - Cinemachine实现相机抖动

    普通相机抖动脚本较易实现,但在使用cinemachine相机下,其Transform组件不可被代码改变,那么Cinemachine的相机抖动如何实现呢?本文结合实际项目,对实现相机抖动的三大步骤进行系 ...