项目框架构建之6:编写通用主机基础类

本文是“项目框架构建”系列之6,本文介绍如何编写通用主机基础类。

1.为了构建通用主机,我们先创建主机接口IAppHost接口

接口需要有配置项,我们定义为HostConfiguration,比如我们希望用户可以设定他的工作目录,就可以放在这里


接口需要有加载的.json的应用程序配置IConfiguration对象
接口还需要有服务管理器IServiceProvider对象
以及接口需要有日志管理ILoggerManager对象


2.构建主机,需要执行方法,所以我们添加了Build()和Run()两个方法
Build()方法是为了生成主机,这是必要方法。
Run()方法是为了启动应用程序,但这并不是必要的,因为主机生成后,您本身可以随时随地去启动你的程序。


3.生命周期以及事件

像主机这种底层结构,为了兼顾扩展性,我们需要提供周期的事件,以便让用户在启动的时候进行参与。


本程序抛砖引玉,提供了4个事件:
HostConfiguring:正在配置IConfiguration中,您可以继续加入其它初始化工作,如加载其它的.json文件
HostConfigurationInitialized:主机的配置已完成且抽象接口IConfiguration已构建
HostServicesInitializing:主机正在构建服务中,您可以继续加入其它服务一起构建
HostReady:主机构建完成已准备就绪


4.扩展依赖注入

为了防止重复添加服务,我们还需要扩展新增一些方法,用于防止重复添加服务。


5.扩展appsettings.cs文件的内容更改

程序运行后,有时可能需要在运行中修改appsettings.json中的配置,但IConfiguration本身不带有这种功能,所以我们需要自行编写扩展


6.编写主机接口IAppHost的抽象实现

我们是通用框架项目,所以需要提供一个抽象的基础实现,依据之前的设计,我们实现它的方法、事件、属性。

我们限定构造函数


程序初始化,初始化应用程序配置以及基础服务


编写Build()方法实现主机的生成


由于Run()函数不是必须的,所以我们把它定义为虚方法


 

下面通过编写事件的虚方法,以便子类能够参与过程

最后,附上完整的主机项目结构图:

也许各位朋友会想要源码,不要慌,等系列文章差不多了后,会开放源码下载。现在也没多少代码,没啥看头的。

下面附上IAppHost的具体实现:

using System;
using System.IO;
using Xejen.Hosting.Extensions;
using Xejen.Logger;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Xejen.Hosting
{
    /// <summary>
    /// <inheritdoc cref="IAppHost"/>基类 
    /// </summary>
    /// <creator>marc</creator>
    public abstract class AppHostBase : IAppHost
    {
        private readonly ILogger _logger;
        private readonly ILoggerManager _loggerManager;
        /// <inheritdoc cref="ILogger"/>
        protected ILogger Logger => _logger;

        /// <summary>
        /// 服务列表
        /// </summary>
        internal IServiceCollection Services;

        /// <inheritdoc cref="HostConfiguration"/>
        public HostConfiguration HostConfig { get; private set; }
        /// <inheritdoc/>
        public IConfiguration Configuration { get; private set; }
        /// <inheritdoc/>
        public IServiceProvider ServiceProvider { get; private set; }
        /// <inheritdoc/>
        public ILoggerManager LoggerManager => _loggerManager;

        /// <inheritdoc/>
        public event EventHandler<IConfigurationBuilder> HostConfiguring;
        /// <inheritdoc/>
        public event EventHandler<IConfiguration> HostConfigurationInitialized;
        /// <inheritdoc/>
        public event EventHandler<IServiceCollection> HostServicesInitializing;
        /// <inheritdoc/>
        public event EventHandler HostReady;

        /// <inheritdoc cref="AppHostBase"/>
        /// <param name="args">启动参数</param>
        /// <param name="loggerManager">采用的日志方式</param>
        protected AppHostBase(string[] args, ILoggerManager loggerManager) : this(loggerManager)
        {
            HostConfig = HostConfiguration.Load(args);

            Initialize();
        }

        /// <summary>
        /// 将 ILoggerManager 添加到构造函数中
        /// </summary>
        /// <param name="loggerManager"><inheritdoc cref="ILoggerManager" path="/summary"/></param>
        private AppHostBase(ILoggerManager loggerManager)
        {
            Check.NotNull(loggerManager, nameof(loggerManager));

            _loggerManager = loggerManager;
            _logger = _loggerManager.CreateLogger(typeof(AppHostBase));
        }

        /// <inheritdoc cref="AppHostBase"/>
        /// <param name="configuration"><inheritdoc cref="HostConfiguration" path="/summary"/></param>
        /// <param name="loggerManager"><inheritdoc cref="ILoggerManager" path="/summary"/></param>
        protected AppHostBase(HostConfiguration configuration, ILoggerManager loggerManager) : this(loggerManager)
        {
            Check.NotNull(configuration, nameof(configuration));

            HostConfig = configuration;

            Initialize();
        }

        private void Initialize()
        {
            _logger.LogInformation($"应用程始启动,开始初始化");

            InitializeConfiguration(HostConfig.ConfigDirectory);
            InitializeServices();
        }

        /// <inheritdoc/>
        public IAppHost Build()
        {
            ConfigureServices(Services);

            ServiceProvider = Services.BuildServiceProvider();

            _logger.LogInformation($"服务初始化完毕,共 {Services.Count} 项服务");

            _logger.LogInformation($"程序初始化完毕");

            OnHostReady(this);

            return this;
        }

        /// <inheritdoc/>
        public virtual void Run() { }

        private void InitializeConfiguration(string configDirectory)
        {
            _logger.LogInformation($"准备加载配置文件");
            if (string.IsNullOrEmpty(configDirectory))
            {
                configDirectory = AppDomain.CurrentDomain.BaseDirectory;
            }

            string appsettingFileName = HostConfig.DefaultSettingsFileName;
            if (!File.Exists(Path.Combine(configDirectory, appsettingFileName)))
            {
                throw new FileNotFoundException($"未找到 {appsettingFileName} 文件,查找目录在:\r\n{configDirectory}");
            }

            var builder = new ConfigurationBuilder()
                .SetBasePath(configDirectory)
                .AddJsonFile(appsettingFileName, optional: true, reloadOnChange: true);

            ConfigureJsonFiles(builder);

            OnHostConfiguring(this, builder);

            Configuration = builder.Build();

            OnHostConfigurationInitialized(this, Configuration);

            _logger.LogInformation($"配置文件加载完成,工作目录: {configDirectory}");
        }

        /// <summary>
        /// 配置json文件,基类已自动完成<see langword="appsettings.json"/>文件的加载,您只需加载您其它的配置文件即可
        /// </summary>
        /// <param name="configuration">传入过来的<see cref="IConfigurationBuilder"/>接口对象</param>
        protected virtual void ConfigureJsonFiles(IConfigurationBuilder configuration)
        {

        }

        /// <summary>
        /// 初始化基础服务
        /// </summary>
        private void InitializeServices()
        {
            _logger.LogInformation($"准备初始化服务");
            var services = new ServiceCollection();
            Services = services;

            // 添加服务
            // services.AddTransient<IService, ServiceImplementation>();
            // 添加主机服务:public class AppHostedService : IHostedService,该类要实现StartAsync以及StopAsync方法
            // services.AddHostedService<AppHostedService>();

            services.AddSingleton(_loggerManager);
            services.AddSingleton(Configuration);
            services.AddSingleton(HostConfig);

            OnHostServicesInitializing(this, services);
        }

        /// <summary>
        /// 配置服务,各子项目可以重写此方法来注册各项服务
        /// </summary>
        /// <param name="services">服务集合</param>
        protected virtual void ConfigureServices(IServiceCollection services)
        {

        }

        /// <inheritdoc cref="HostConfiguring" path="/summary"/>
        /// <param name="sender"><inheritdoc cref="IAppHost" path="/summary"/></param>
        /// <param name="builder">用于构建应用程序配置的构建器</param>
        protected virtual void OnHostConfiguring(IAppHost sender, IConfigurationBuilder builder)
        {
            HostConfiguring?.Invoke(sender, builder);
        }

        /// <inheritdoc cref="HostConfigurationInitialized" path="/summary"/>
        /// <param name="sender"><inheritdoc cref="IAppHost" path="/summary"/></param>
        /// <param name="builder">应用程序配置</param>
        protected virtual void OnHostConfigurationInitialized(IAppHost sender, IConfiguration builder)
        {
            HostConfigurationInitialized?.Invoke(sender, builder);
        }

        /// <inheritdoc cref="HostServicesInitializing" path="/summary"/>
        /// <param name="sender"><inheritdoc cref="IAppHost" path="/summary"/></param>
        /// <param name="builder">服务列表</param>
        protected virtual void OnHostServicesInitializing(IAppHost sender, IServiceCollection builder)
        {
            HostServicesInitializing?.Invoke(sender, builder);
        }

        /// <inheritdoc cref="HostReady" path="/summary"/>
        /// <param name="sender"><inheritdoc cref="IAppHost" path="/summary"/></param>
        protected virtual void OnHostReady(IAppHost sender)
        {
            HostReady?.Invoke(sender, EventArgs.Empty);
        }
    }
}

祝您用餐愉快,下一篇我们将编写如何使用主机框架的实际应用,敬请期待

1-3-5 $ 3-5-5-4 带着田螺回四堡 3-5-2-4

相关推荐

  1. 编程笔记 Golang基础 012 项目构建

    2024-01-05 15:20:04       29 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-05 15:20:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-05 15:20:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-05 15:20:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-05 15:20:04       18 阅读

热门阅读

  1. WPF中MVVM使用总结

    2024-01-05 15:20:04       42 阅读
  2. 【WPF.NET开发】WPF中的焦点

    2024-01-05 15:20:04       33 阅读
  3. 面试 Vue 框架八股文十问十答第二期

    2024-01-05 15:20:04       43 阅读
  4. onvif学习记录

    2024-01-05 15:20:04       42 阅读
  5. 测试:抓包工具

    2024-01-05 15:20:04       41 阅读
  6. 【2024.01.02】刷算法07

    2024-01-05 15:20:04       33 阅读
  7. Linux 命令汇总

    2024-01-05 15:20:04       32 阅读