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. [考试反思]1031csp-s模拟测试95:优势

    假的三首杀.因为交文件所以他们都不往OJ上交. 假装是三首杀吧.嗯. 然而昨天还是没有AK.好像说是按照64位评测机的评测结果为准. 但是联赛省选的机子好像都是32位的?也就是和我们正在用的一致. 所 ...

  2. CSPS模拟 84

    整场考试就一个字虚 真的啥也不会 T1 80很好打 可是100这鬼畜的数据范围...二分答案? 没做过蚯蚓跪..果然多刷题有好处.. 于是死在80分处 T2 56很好打 可是100这鬼畜....... ...

  3. VIM编辑器的常用命令及使用!

    在热门Linux操作系统中都会默认安装一款超好用的文本编辑器——名字叫“vim”,vim是vi编辑器的升级版. Vim能够得到这么多厂商与用户的认可,原因就是在Vim编辑器中有三种模式——命令模式.末 ...

  4. Vim 便捷 | 自己的Vim

    在.vimrc中安排自己的Vim set softtabstop=4 "将连续数量的空格视为一个制表符 set shiftwidth=4 "自动缩进所使用的空白数 set auto ...

  5. linux防火墙相关。

    ubuntu 系统默认已安装ufw. 1.安装 sudo apt-get install ufw 2.启用 sudo ufw enable sudo ufw default deny 运行以上两条命令 ...

  6. css3 svg 物体跟随路径动画教程

    css3 svg 物体跟随路径动画教程https://www.jianshu.com/p/992488f3f3fc

  7. python Django框架正式准备工作

    之前由于不太了解数据库方面的知识,但经过一段时间的web应用的开发学习,成功的用其他框架连接了数据库,并完成了相关操作,数据爬取也初识了,更了解了python这门语言的语法,但路还很长,因此现在才能正 ...

  8. 网络编程--UDP通讯

    UTP传输 public class Send1 { public static void main(String[] args) throws Exception { Scanner sc=new ...

  9. 【algo&ds】3.栈和队列

    1.堆栈 堆栈(Stack):具有一定操作约束的线性表(只在一端(栈顶,Top)做插入.删除) 先进后出特性 1.1堆栈的抽象数据类型描述 类型名称: 堆栈(Stack) 数据对象集:一个有0个或多个 ...

  10. 简单的倒叙应用---倒序打印字符串(C语言)

    void reverseStr(char* str){ if(*str=='\0'){ return; } reverseStr(str+1); printf("%c\n",*st ...