[ASP.NET Core 3框架揭秘]服务承载系统[5]: 承载服务启动流程[上篇]

我们在《总体设计[上篇]》和《总体设计[下篇]》中通过对IHostedService、IHost和IHostBuider这三个接口的介绍让读者朋友们对服务承载模型有了大致的了解。接下来我们从抽象转向具体,看看承载系统针对该模型的实现是如何落地的。要了解承载模型的默认实现,只需要了解IHost接口和IHostBuilder的默认实现类型就可以了。从下图所示的UML可以看出,这两个接口的默认实现类型分别是Host和HostBuilder,本篇将会着重介绍这两个类型。

一、服务宿主

Host类型是对IHost接口的默认实现,它仅仅是定义在NuGet包“Microsoft.Extensions.Hosting”中的一个内部类型,由于我们在本节最后还会涉及另一个同名的公共静态类型,在容易出现混淆的地方,我们会将它称为“实例类型Host”以示区别。在正式介绍Host类型的具体实现之前,我们得先来认识两个相关的类型,其中一个是承载相关配置选项的HostOptions。如下面的代码片段所示,HostOptions仅包含唯一的属性ShutdownTimeout表示关闭Host对象的超时时限,它的默认值为5秒钟。

public class HostOptions
{
    public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5);
}

我们在《总体设计[上篇]》已经认识了一个与承载应用生命周期相关的IHostApplicationLifetime接口,Host类型还涉及到另一个与生命周期相关的IHostLifetime接口。当我们调用Host对象的StartAsync方法将它启动之后,该方法会先调用IHostLifetime服务的WaitForStartAsync方法。当Host对象的StopAsync方法在执行过程中,如果它成功关闭了所有承载的服务,注册IHostLifetime服务的StopAsync方法会被调用。

public interface IHostLifetime
{
    Task WaitForStartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

在《承载长时间运行的服务[下篇]》进行日志编程的演示时,程序启动后控制台上会输出三条级别为Information的日志,其中第一条日志的内容为“Application started. Press Ctrl+C to shut down.”,后面两条则会输出当前的承载环境的信息和存放内容文件的根目录路径。当应用程序关闭之前,控制台上还会出现一条内容为“Application is shutting down...”的日志。上述这四条日志在控制台上输出额效果体现在下图中。

上图所示的四条日志都是如下这个ConsoleLifetime对象输出的,ConsoleLifetime类型是对IHostLifetime接口的实现。除了以日志的形式输出与当前承载应用程序相关的状态信息之外,针对Cancel按键(Ctrl + C)的捕捉以及随后关闭当前应用的功能也实现在ConsoleLifetime类型中。ConsoleLifetime采用的配置选项定义在ConsoleLifetimeOptions类型中,该类型唯一的属性成员SuppressStatusMessages用来决定上述四条日志是否需要被输出。

public class ConsoleLifetime : IHostLifetime, IDisposable
{
    public ConsoleLifetime(IOptions<ConsoleLifetimeOptions> options, IHostEnvironment environment, IHostApplicationLifetime applicationLifetime);
    public ConsoleLifetime(IOptions<ConsoleLifetimeOptions> options, IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory);

    public Task StopAsync(CancellationToken cancellationToken);
    public Task WaitForStartAsync(CancellationToken cancellationToken);
    public void Dispose();
}

public class ConsoleLifetimeOptions
{
    public bool SuppressStatusMessages { get; set; }
}

下面的代码片段展示的是经过简化的Host类型的定义。Host类型的构造函数中注入了一系列依赖服务,其中包括作为依赖注入容器的IServiceProvider对象,用来记录日志的ILogger<Host>对象和提供配置选项的IOptions<HostOptions>对象,以及两个与生命周期相关的IHostApplicationLifetime对象和IHostLifetime对象。值得一提的是,这里提供的IHostApplicationLifetime对象的类型必需是ApplicationLifetime,因为它需要调用其NotifyStarted和NotifyStopped方法在应用程序启动和关闭之后向订阅者发出通知,但是这两个方法并没有定义在IHostApplicationLifetime接口中。

internal class Host : IHost
{
    private readonly ILogger<Host> _logger;
    private readonly IHostLifetime _hostLifetime;
    private readonly ApplicationLifetime _applicationLifetime;
    private readonly HostOptions _options;
    private IEnumerable<IHostedService> _hostedServices;

    public IServiceProvider Services { get; }

    public Host(IServiceProvider services, IHostApplicationLifetime applicationLifetime, ILogger<Host> logger, IHostLifetime hostLifetime, IOptions<HostOptions> options)
    {
        Services = services;
        _applicationLifetime = (ApplicationLifetime)applicationLifetime;
        _logger = logger;
        _hostLifetime = hostLifetime;
        _options = options.Value);
    }

    public async Task StartAsync(CancellationToken cancellationToken = default)
    {
        await _hostLifetime.WaitForStartAsync(cancellationToken);
        cancellationToken.ThrowIfCancellationRequested();
        _hostedServices = Services.GetService<IEnumerable<IHostedService>>();
        foreach (var hostedService in _hostedServices)
        {
            await hostedService.StartAsync(cancellationToken).ConfigureAwait(false);
        }
        _applicationLifetime?.NotifyStarted();
    }

    public async Task StopAsync(CancellationToken cancellationToken = default)
    {
        using (var cts = new CancellationTokenSource(_options.ShutdownTimeout))
        using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken))
        {
            var token = linkedCts.Token;
            _applicationLifetime?.StopApplication();
            foreach (var hostedService in _hostedServices.Reverse())
            {
                await hostedService.StopAsync(token).ConfigureAwait(false);
            }

            token.ThrowIfCancellationRequested();
            await _hostLifetime.StopAsync(token);
            _applicationLifetime?.NotifyStopped();
        }
    }

    public void Dispose() => (Services as IDisposable)?.Dispose();
}

在实现的StartAsync中,Host对象率先调用了IHostLifetime对象的WaitForStartAsync方法。如果注册的服务类型为ConsoleLifetime,它会输出前面提及的三条日志。于此同时,ConsoleLifetime对象还会注册控制台的按键事件,其目的在于确保在用户按下取消组合键(Ctrl + C)后应用能够被正常关闭。

Host对象会利用作为依赖注入容器的IServiceProvider对象提取出代表承载服务的所有IHostedService对象,并通过StartAsync方法来启动它们。当所有承载的服务正常启动之后,ApplicationLifetime对象的NotifyStarted方法会被调用,此时订阅者会接收到应用启动的通知。有一点需要着重指出:代表承载服务的所有IHostedService对象是“逐个(不是并发)”被启动的,而且只有等待所有承载服务全部被启动之后,我们的应用程序才算成功启动了。在整个启动过程中,如果利用作为参数的CancellationToken接收到取消请求,启动操作会中止。

当Host对象的StopAsync方法被调用的时候,它会调用ApplicationLifetime对象的StopApplication方法对外发出应用程序即将被关闭的通知,此后它会调用每个IHostedService对象的StopAsync方法。当所有承载服务被成功关闭之后,Host对象会先后调用IHostLifetime对象的StopAsync和ApplicationLifetime对象的NotifyStopped方法。在Host关闭过程中,如果超出了通过HostOptions配置选项设定的超时时限,或者利用作为参数的CancellationToken接收到取消请求,整个过程会中止。

二、针对配置系统的设置

作为服务宿主的IHost对象总是通过对应的IHostBuilder对象构建出来的,上面这个Host类型对应的IHostBuilder实现类型为HostBuilder,我们接下来就来探讨一下Host对象是如何HostBuilder对象构建出来的。除了用于构建IHost对象的Build方法,IHostBuilder接口还定义了一系列的方法使我们可以对最终提供的IHost对象作相应的前期设置,这些设置将会被缓存起来最后应用到Build方法上。

我们先来介绍HostBuilder针对配置系统的设置。如下面的代码片段所示,ConfigureHostConfiguration方法中针对面向宿主配置和ConfigureAppConfiguration方法面向应用配置提供的委托对象都暂存在对应集合对象中,对应的字段分别是configureHostConfigActions和configureAppConfigActions。

public class HostBuilder : IHostBuilder
{
    private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
    private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();

    public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();

    public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
    {
        _configureHostConfigActions.Add(configureDelegate);
        return this;
    }

    public IHostBuilder ConfigureAppConfiguration(
        Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
    {
        _configureAppConfigActions.Add(configureDelegate);
        return this;
    }
    …
}

IHostBuilder接口上的很多方法都与依赖注入有关。针对依赖注入框架的设置主要体现在两个方面:其一,利用ConfigureServices方法添加服务注册;其二,利用两个UseServiceProviderFactory<TContainerBuilder>方法注册IServiceProviderFactory<TContainerBuilder>工厂,以及利用ConfigureContainer<TContainerBuilder>方对该工厂创建的ContainerBuilder作进一步设置。

三、注册依赖服务

与针对配置系统的设置一样,ConfigureServices方法中用来注册依赖服务的Action<HostBuilderContext, IServiceCollection>委托对象同样被暂存在对应的字段configureServicesActions表示的集合中,它们最终会在Build方法中被使用。

public class HostBuilder : IHostBuilder
{
    private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();

    public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
    {
        _configureServicesActions.Add(configureDelegate);
        return this;
    }
    …
}

除了直接调用IHostBuilder接口的ConfigureServices方法进行服务注册之外,我们还可以调用如下这些扩展方法完成针对某些特殊服务的注册。两个ConfigureLogging扩展方法重载帮助我们注册针对日志框架相关的服务,两个UseConsoleLifetime扩展方法重载添加的是针对ConsoleLifetime的服务注册,两个RunConsoleAsync扩展方法重载则在注册ConsoleLifetime服务的基础上,进一步构建并启动作为宿主的IHost对象。

public static class HostingHostBuilderExtensions
{
    public static IHostBuilder ConfigureLogging(this IHostBuilder hostBuilder, Action<HostBuilderContext, ILoggingBuilder> configureLogging)
    => hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(context, builder)));

    public static IHostBuilder ConfigureLogging(this IHostBuilder hostBuilder, Action<ILoggingBuilder> configureLogging)
    => hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(builder)));

    public static IHostBuilder UseConsoleLifetime(this IHostBuilder hostBuilder)
    =>  hostBuilder.ConfigureServices((context, collection) => collection.AddSingleton<IHostLifetime, ConsoleLifetime>());

    public static IHostBuilder UseConsoleLifetime(this IHostBuilder hostBuilder, Action<ConsoleLifetimeOptions> configureOptions)
    =>  hostBuilder.ConfigureServices((context, collection) =>
        {
            collection.AddSingleton<IHostLifetime, ConsoleLifetime>();
            collection.Configure(configureOptions);
        });

    public static Task RunConsoleAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
    =>  hostBuilder.UseConsoleLifetime().Build().RunAsync(cancellationToken);

    public static Task RunConsoleAsync(this IHostBuilder hostBuilder, Action<ConsoleLifetimeOptions> configureOptions, CancellationToken cancellationToken = default)
    =>  hostBuilder.UseConsoleLifetime(configureOptions).Build().RunAsync(cancellationToken);
}

四、注册IServiceProviderFactory<TContainerBuilder>

作为依赖注入容器的IServiceProvider对象总是由注册的IServiceProviderFactory<TContainerBuilder>工厂创建的。由于UseServiceProviderFactory<TContainerBuilder>方法注册的IServiceProviderFactory<TContainerBuilder>是个泛型对象,所以HostBuilder会将它转换成如下这个IServiceFactoryAdapter接口类型作为适配。如下面的代码片段所示,它仅仅是将ContainerBuilder转换成Object类型而已。ServiceFactoryAdapter<TContainerBuilder>类型是对IServiceFactoryAdapter接口的默认实现。

internal class ServiceFactoryAdapter<TContainerBuilder> : IServiceFactoryAdapter
{
    private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory;
    private readonly Func<HostBuilderContext> _contextResolver;
    private Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> _factoryResolver;

    public ServiceFactoryAdapter(IServiceProviderFactory<TContainerBuilder> serviceProviderFactory)
    => _serviceProviderFactory = serviceProviderFactory;

    public ServiceFactoryAdapter(Func<HostBuilderContext> contextResolver, Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factoryResolver)
    {
        _contextResolver = contextResolver;
        _factoryResolver = factoryResolver;
    }

    public object CreateBuilder(IServiceCollection services)
        => _serviceProviderFactory ?? _factoryResolver(_contextResolver()).CreateBuilder(services);

    public IServiceProvider CreateServiceProvider(object containerBuilder)

        => _serviceProviderFactory.CreateServiceProvider((TContainerBuilder)containerBuilder);
}

如下所示的是两个UseServiceProviderFactory<TContainerBuilder>重载的定义,第一个方法重载提供的IServiceProviderFactory<TContainerBuilder>对象和第二个方法重载提供的Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>>会被转换成一个ServiceFactoryAdapter<TContainerBuilder>对象并通过_serviceProviderFactory字段暂存起来。如果UseServiceProviderFactory<TContainerBuilder>方法并没有被调用,_serviceProviderFactory 字段返回的将是根据DefaultServiceProviderFactory对象创建的ServiceFactoryAdapter<IServiceCollection>对象,下面给出的代码片段也体现了这一点。

public class HostBuilder : IHostBuilder
{
    private List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();
    private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());

    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
    {
        _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory);
        return this;
    }

    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
    {
        _serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(() => _hostBuilderContext, factory));
        return this;
    }
}

注册IServiceProviderFactory<TContainerBuilder>工厂提供的TContainerBuilder对象可以通过ConfigureContainer<TContainerBuilder>方法做进一步设置,具体的设置由提供的Action<HostBuilderContext, TContainerBuilder>对象来完成。这个泛型的委托对象同样需要做类似的适配才能被暂存起来,它最终转换成如下IConfigureContainerAdapter接口类型,这个适配本质上也是将TContainerBuilder对象转换成了Object类型。如下所示的ConfigureContainerAdapter<TContainerBuilder>类型是对这个接口的默认实现。

internal interface IServiceFactoryAdapter
{
    object CreateBuilder(IServiceCollection services);
    IServiceProvider CreateServiceProvider(object containerBuilder);
}

internal interface IConfigureContainerAdapter
{
    void ConfigureContainer(HostBuilderContext hostContext, object containerBuilder);
}

internal class ConfigureContainerAdapter<TContainerBuilder> : IConfigureContainerAdapter
{
    private Action<HostBuilderContext, TContainerBuilder> _action;
    public ConfigureContainerAdapter(Action<HostBuilderContext, TContainerBuilder> action)
        => _action = action;
    public void ConfigureContainer(HostBuilderContext hostContext, object containerBuilder)
        => _action(hostContext, (TContainerBuilder)containerBuilder);
}

如下所示的是ConfigureContainer<TContainerBuilder>方法的定义,我们会发现该方法会将提供的Action<HostBuilderContext, TContainerBuilder>对象转换成ConfigureContainerAdapter<TContainerBuilder>对象,并添加到通过configureContainerActions字段表示的集合中。

public class HostBuilder : IHostBuilder
{
    private List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();
    public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
    {
        _configureContainerActions.Add(new ConfigureContainerAdapter<TContainerBuilder>(configureDelegate));
        return this;
    }
    …
}

五、与第三方依赖注入框架的整合

我们在《一个Mini版的依赖注入框架》中创建了一个名为Cat的简易版依赖注入框架,并在《与第三方依赖注入框架的适配》中为它创建了一个IServiceProviderFactory<TContainerBuilder>实现,具体类型为CatServiceProvider,接下来我们演示一下如何通过注册这个CatServiceProvider实现与Cat这个第三方依赖注入框架的整合。如果使用Cat框架,我们可以在服务类型上标注MapToAttribute特性的方式来定义服务注册信息。在创建的演示程序中,我们采用这样的方式定义了三个服务(Foo、Bar和Baz)和对应的接口(IFoo、IBar和IBaz)。

public interface IFoo { }
public interface IBar { }
public interface IBaz { }

[MapTo(typeof(IFoo), Lifetime.Root)]
public class Foo :  IFoo { }

[MapTo(typeof(IBar), Lifetime.Root)]
public class Bar :  IBar { }

[MapTo(typeof(IBaz), Lifetime.Root)]
public class Baz :  IBaz { }

如下所示的FakeHostedService表示我们演示的应用程序承载的服务。我们在构造函数中注入了上面定义的三个服务,构造函数提供的调试断言确保这三个服务被成功注入。

public sealed class FakeHostedService: IHostedService
{
    public FakeHostedService(IFoo foo, IBar bar, IBaz baz)
    {
        Debug.Assert(foo != null);
        Debug.Assert(bar != null);
        Debug.Assert(baz != null);
    }
    public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

在如下所示的服务承载程序中,我们创建了一个HostBuilder对象,并通过调用ConfigureServices方法注册了需要承载的FakeHostedService服务。我们接下来调用UseServiceProviderFactory方法完成了对CatServiceProvider的注册,并在随后调用了CatBuilder的Register方法完成了针对入口程序集的批量服务注册。当我们调用HostBuilder的Build方法构建出作为宿主的Host对象并启动它之后,承载的FakeHostedService服务将自动被创建并启动。(源代码从这里下载)

class Program
{
    static void Main()
    {
        new HostBuilder()
            .ConfigureServices(svcs => svcs.AddHostedService<FakeHostedService>())
            .UseServiceProviderFactory(new CatServiceProviderFactory())
            .ConfigureContainer<CatBuilder>(builder=>builder.Register(Assembly.GetEntryAssembly()))
            .Build()
            .Run();
    }
}

服务承载系统[1]: 承载长时间运行的服务[上篇]
服务承载系统[2]: 承载长时间运行的服务[下篇]
服务承载系统[3]: 总体设计[上篇]
服务承载系统[4]: 总体设计[下篇]
服务承载系统[5]: 承载服务启动流程[上篇]
服务承载系统[6]: 承载服务启动流程[下篇]

原文地址:https://www.cnblogs.com/artech/p/inside-asp-net-core-09-05.html

时间: 2024-08-02 23:30:44

[ASP.NET Core 3框架揭秘]服务承载系统[5]: 承载服务启动流程[上篇]的相关文章

[ASP.NET Core 3框架揭秘] Options[4]: Options模型[下篇]

六.IOptionsMonitorCache<TOptions> IOptionsFactory<TOptions>解决了Options的创建与初始化问题,但由于它自身是无状态的,所以Options模型对Options对象实施缓存可以获得更好的性能.Options模型中针对Options对象的缓存由IOptionsMonitorCache<TOptions>对象来完成,如下所示的代码片段是该接口的定义. public interface IOptionsMonitorC

[ASP.NET Core 3框架揭秘] Options[7]: 与配置系统的整合

Options模型本身与配置系统完全没有关系,但是配置在大部分情况下会作为绑定Options对象的数据源,所以有必要将两者结合在一起.与<扩展与定制>演示的两个例子一样,针对配置系统的集成同样是通过定制Options模型相应的对象来实现的.具体来说,集成配置系统需要解决如下两个问题: 将承载配置数据的IConfiguration对象绑定为Options对象. 自动感知配置数据的变化. 第一个问题涉及针对Options对象的初始化问题,这自然是通过自定义IConfigureOptions<

[ASP.NET Core 3框架揭秘] 依赖注入[5]: 利用容器提供服务

毫不夸张地说,整个ASP.NET Core框架是建立在依赖注入框架之上的.ASP.NET Core应用在启动时构建管道以及利用该管道处理每个请求过程中使用到的服务对象均来源于依赖注入容器.该依赖注入容器不仅为ASP.NET Core框架自身提供必要的服务,同时也是应用程序的服务提供者,依赖注入已经成为了ASP.NET Core应用的基本编程模式. 一.服务的注册与消费 为了让读者朋友们能够更加容易地认识.NET Core提供的依赖注入框架,我在"<一个迷你版DI框架>"中特

[ASP.NET Core 3框架揭秘] 依赖注入:控制反转

ASP.NET Core框架建立在一些核心的基础框架之上,这些基础框架包括依赖注入.文件系统.配置选项和诊断日志等.这些框架不仅仅是支撑ASP.NET Core框架的基础,我们在进行应用开发的时候同样会频繁地使用到它们.对于这里提到的这几个基础框架,依赖注入尤为重要.ASP.NET Core应用在启动以及后续针对请求的处理过程中,它会依赖各种的组件提供服务.为了便于定制,这些组件一般会以接口的形式进行"标准化",我们将这些标准化的组件统一称为"服务(Service)"

[ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式

原文:[ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式 IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架之中以实现对流程的复用,并按照"好莱坞法则"实现应用程序的代码与框架之间的交互.我们可以采用若干设计模式以不同的方式实现IoC,比如我们在前面介绍的模板方法.工厂方法和抽象工厂,接下来我们介绍一种更有价值的IoC模式:依赖注入(DI:Dependency Injection). 一.由容器提供对象 和前面介绍的工厂方法和抽象工厂模式一样,依

[ASP.NET Core 3框架揭秘] 依赖注入:一个Mini版的依赖注入框架

在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍.为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类似的原理创建了一个简易版本的依赖注入框架,也就是我们在前面多次提及的Cat. 源代码下载 普通服务的注册与消费泛型服务的注册与消费多服务实例的提供服务实例的生命周期 一.编程体验 虽然我们对这个名为Cat的依赖注入框架进行了最大限度的简化,但是与.NET Core框架内部使用的真实依赖注入框架相比,

[ASP.NET Core 3框架揭秘] 配置[3]:配置模型总体设计

原文:[ASP.NET Core 3框架揭秘] 配置[3]:配置模型总体设计 在<读取配置数据>([上篇],[下篇])上面一节中,我们通过实例的方式演示了几种典型的配置读取方式,接下来我们从设计的维度来重写认识配置模型.配置的编程模型涉及到三个核心对象,分别通过三个对应的接口(IConfiguration.IConfigurationSource和IConfigurationBuilder)来表示.如果从设计层面来审视背后的配置模型,还缺少另一个名通过IConfigurationProvide

[ASP.NET Core 3框架揭秘] 配置[6]:多样化的配置源[上篇]

.NET Core采用的这个全新的配置模型的一个主要的特点就是对多种不同配置源的支持.我们可以将内存变量.命令行参数.环境变量和物理文件作为原始配置数据的来源.如果采用物理文件作为配置源,我们可以选择不同的格式(比如XML.JSON和INI等).如果这些默认支持的配置源形式还不能满足你的需求,我们还可以通过注册自定义IConfigurationSource的方式将其他形式数据作为配置来源. 一.MemoryConfigurationSource 在之前的实例演示都在使用MemoryConfigu

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [上篇]

微软在千禧年推出 .NET战略,并在两年后推出第一个版本的.NET Framework和IDE(Visual Studio.NET 2002,后来改名为Visual Studio),如果你是一个资深的.NET程序员,相信传统的.NET应用的开发方式已经深深地烙印在你的脑子里面..NET Core带来了全新的开发体验,但开发方式的差异根本不足以成为你快速跨入.NET Core 世界的门槛,因为在.NET Core在很多方面比传统的.NET Framework应用开发要简单.为了消除很多尚未接触过.

[ASP.NET Core 3框架揭秘] 跨平台开发体验: Linux

如果想体验Linux环境下开发.NET Core应用,我们有多种选择.一种就是在一台物理机上安装原生的Linux,我们可以根据自身的喜好选择某种Linux Distribution,目前来说像RHEL.Ubuntu.Debian.Fedora.CentOS和SUSE这些主流的Distribution都是支持的.如果读者朋友们觉得这种方式比较麻烦,我们也可以采用虚拟机的形式安装相应的Linux Distribution,比如我经常使用的都是安装在VirtualBox上的Ubuntu.对于X64 W