当前位置: 首页 > news >正文

第17章 配置类的反射方式实例化、单例和依赖注入

1 Core .Configuration.IConfig

using Newtonsoft.Json;

namespace Core.Configuration

{

    /// <summary>

    /// 【配置--接口】

    /// <remarks>

    /// 摘要:

    ///    通过继承于该接口的具体实现类中的属性成员实例实现与“appsettings.json”文件中1具指定的JSON根节点中的相关数据进行读写操作,为当程序中指定中间件的实例化提供数据支撑。

    /// </remarks>

    /// </summary>

    public interface IConfig

    {

        /// <summary>

        /// 【名称】

        /// <remarks>

        /// 摘要:

        ///    “appsettings.json”文件中获取1JSON/值对中的1个指定且唯一的JSON根节点的字符串值。

        /// JsonIgnore特性:

        ///     当前“DataConfig”类的实例通过JSON中的方法进行序列化时,该属性成员及其实例值将不会被序列化,更不会被持久化到“appsettings.json”文件中,

        ///  即该属性成员实例设定1个指定且唯一的JSON根节点,而不是在“appsettings.json”文件指定的JSON根节点中设定: "Name": GetType().Name JSON/值对。

        /// </remarks>

        /// </summary>

        [JsonIgnore]

        string Name => GetType().Name;

        ///<summary>

        /// 【获取顺序】

        /// <remarks>

        /// 摘要:

        ///    在对所有继承于“IConfig”接口的具体实现类的所有实例进行排序操作时,设定指定具体实现类的实例在所有实例中的次序,默认值:1

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     指定具体实现类的实例在所有实例中的排序的顺序值:1

        /// </returns>

        /// </summary>

        public int GetOrder() => 1;

    }

}

2 Core .Configuration.AppSettings

using Newtonsoft.Json;

using Newtonsoft.Json.Linq;

namespace Core.Configuration

{

    /// <summary>

    /// 【应用配置--类】

    /// <remarks>

    /// 摘要:

    ///    把当前程序中所有继承了“IConfig”接口的具体实现类的实例以键/值对的形式存储到当前类的的变量和属性字典成员实例中。

    /// </remarks>

    /// </summary>

    public class AppSettings

    {

        #region 变量--私有/保护

        /// <summary>

        /// 【配置集】

        /// <remarks>

        /// 摘要:

        ///    设置字典变量成员实例,该实例以键/值对的形式存储着所有继承了“IConfig”接口的具体实现类的实例。

        /// </remarks>

        /// </summary>

        private readonly Dictionary<Type, IConfig> _configurations = new();

        #endregion

        #region 拷贝构造方法

        /// <param name="configurations">列表接口实例,该实例存储着所有继承于“IConfig”接口的具体实现类的实例</param>

        /// <summary>

        /// 【静态构造方法】

        /// <remarks>

        /// 摘要:

        ///     通过该静态构造方法,对当前类中的电子邮箱正则表达式变量成员进行实例化。

        /// </remarks>

        /// </summary>

        public AppSettings(IList<IConfig> configurations = null)

        {

            _configurations = configurations

                ?.OrderBy(config => config.GetOrder())

                ?.ToDictionary(config => config.GetType(), config => config)

                ?? new Dictionary<Type, IConfig>();

        }

        #endregion

        #region 属性

        /// <summary>

        /// 【配置】

        /// <remarks>

        /// 摘要:

        ///    获取/设置字典属性成员实例,该实例以键/值对的形式存储着所有继承了“IConfig”接口的具体实现类的实例。

        /// 说明:

        ///     该字典属性成员实例所存储到键/值对中的值,经过JSON编码格式的格式化过后的字符串。

        /// </remarks>

        /// </summary>

        [JsonExtensionData]

        public Dictionary<string, JToken> Configuration { get; set; }

        #endregion

        #region 方法

        /// <typeparam name = "TConfig"> 泛型类型实例(这里指定:所有继承于“IConfig”接口的具体实现类)</typeparam>

        /// <summary>

        /// 【获取】

        /// <remarks>

        /// 摘要:

        ///     获取1个指定的继承于“IConfig”接口的具体实现类的类型实例。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///     1个指定的继承于“IConfig”接口的具体实现类的类型实例。

        /// </returns>

        /// </summary>

        public TConfig Get<TConfig>() where TConfig : class, IConfig

        {

            if (_configurations[typeof(TConfig)] is not TConfig config)

                throw new NopException($"当前程序中没有定义继承于 '{typeof(TConfig)}' 的类");

            return config;

        }

        /// <param name="configurations">列表接口实例,该实例存储着所有继承于“IConfig”接口的具体实现类的实例</param>

        /// <summary>

        /// 【更新】

        /// <remarks>

        /// 摘要:

        ///     把继承于“IConfig”接口的具体实现类的实例,以键/值对的形式存储到当前类的字典变量成员实例中。

        /// </remarks>

        /// </summary>

        public void Update(IList<IConfig> configurations)

        {

            foreach (var config in configurations)

            {

                _configurations[config.GetType()] = config;

            }

        }

        #endregion

    }

}

4 Core .Configuration.AppSettingsHelper

using System.Text;

using Core.Infrastructure;

using Newtonsoft.Json;

using Newtonsoft.Json.Linq;

namespace Core.Configuration

{

    /// <summary>

    /// 【应用配置助手--类】

    /// <remarks>

    /// 摘要:

    ///    通过当前类中的成员方法把当前程序中所有继承了“IConfig”接口的具体实现类的实例以键/值对的形式存储到应用配置类的的变量和属性字典成员实例中,如果需要并把这些数据持久化存储到"appsettings.json"文件。

    /// </remarks>

    /// </summary>

    public class AppSettingsHelper

    {

        #region 变量--私有/保护

        /// <summary>

        /// 【配置顺序】

        /// <remarks>

        /// 摘要:

        ///    设置字典变量成员实例,该实例以键/值对的形式存储着所有继承了“IConfig”接口的具体实现类的实例。

        /// </remarks>

        /// </summary>

        private static Dictionary<string, int> _configurationOrder;

        #endregion

        #region 方法

        /// <param name="configurations">列表接口实例,该实例存储着所有继承于“IConfig”接口的具体实现类的实例</param>

        /// <param name="fileProvider">自定义文件提供程序接口的1个指定实例。</param>

        /// <param name="overwrite">指示是否为需要把“appsettings.json”文件中的内容,使用新的内容进行覆盖性替换,默认值:true,即进行覆盖性替换。</param>

        /// <summary>

        /// 【保存应用配置】

        /// <remarks>

        /// 摘要:

        ///     把继承于“IConfig”接口的具体实现类的实例,以键/值对的形式存储到应用配置类的字典变量和属性成员实例中,如果需要并把这些数据持久化存储到"appsettings.json"文件。

        /// </remarks>

        /// <returns>

        /// 返回:

        ///    应用配置类的1个指定实例,该实例中的字典变量和属性成员实例中存储着所有继承了“IConfig”接口的具体实现类的实例。

        /// </returns>

        /// </summary>

        public static AppSettings SaveAppSettings(IList<IConfig> configurations, INopFileProvider fileProvider, bool overwrite = true)

        {

            if (configurations is null)

                throw new ArgumentNullException(nameof(configurations));

            if (_configurationOrder is null)

                _configurationOrder = configurations.ToDictionary(config => config.Name, config => config.GetOrder());

            //把当前应用程序中所有继承了“IConfig”接口的具体实现类的实例,存储到单例实例中。

            var appSettings = Singleton<AppSettings>.Instance ?? new AppSettings();

            appSettings.Update(configurations);

            Singleton<AppSettings>.Instance = appSettings;

            //如果启动项中没有"appsettings.json"文件,则新建该文件。

            // var filePath = fileProvider.MapPath(NopConfigurationDefaults.AppSettingsFilePath);

            var filePath = fileProvider.MapPath("appsettings.json");

            var fileExists = fileProvider.FileExists(filePath);

            fileProvider.CreateFile(filePath);

            //根据"appsettings.json"文件的JSON反序列化操作,对继承于“IConfig”接口的所有具体实现类进行实例化操作。

            var configuration = JsonConvert.DeserializeObject<AppSettings>(fileProvider.ReadAllText(filePath, Encoding.UTF8))

                ?.Configuration

                ?? new();

            //依次把所有继承了“IConfig”接口的具体实现类的实例,经过JSON编码格式的格式化过后的字符串存储到字典键/值对的值中。

            foreach (var config in configurations)

            {

                configuration[config.Name] = JToken.FromObject(config);

            }

            //把所有承于“IConfig”接口的所有具体实现类的实例以顺序方式进行排序(例如:数据库配置类的实例就排第1)

            appSettings.Configuration = configuration

                .SelectMany(outConfig => _configurationOrder.Where(inConfig => inConfig.Key == outConfig.Key).DefaultIfEmpty(),

                    (outConfig, inConfig) => new { OutConfig = outConfig, InConfig = inConfig })

                .OrderBy(config => config.InConfig.Value)

                .Select(config => config.OutConfig)

                .ToDictionary(config => config.Key, config => config.Value);

            //如果不存在"appsettings.json"文件或需要进行覆盖性替换,把所有承于“IConfig”接口的所有具体实现类的实例持久化存储到"appsettings.json"文件中。

            if (!fileExists || overwrite)

            {

                var text = JsonConvert.SerializeObject(appSettings, Formatting.Indented);

                fileProvider.WriteAllText(filePath, text, Encoding.UTF8);

            }

            return appSettings;

        }

        #endregion

    }

}

5 Framework.Infrastructure.Extensions.ServiceCollectionExtensions

using Core;

using Core.Configuration;

using Core.Infrastructure;

using Microsoft.AspNetCore.Builder;

using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.DependencyInjection;

using System.Net;

namespace Framework.Infrastructure.Extensions

{

    /// <summary>

    /// 【服务集合扩展--类】

    /// <remarks>

    /// 摘要:

    ///    通过当前类中的成员方法把一些具体实现类的实例,依赖注入到.Net(Core)内置依赖注入容器实例中。

    /// </remarks>

    /// </summary>

    public static class ServiceCollectionExtensions

    {

        /// <param name="services">.Net(Core)框架内置依赖注入容器实例。</param>

        /// <param name="builder">Web应用构建器的1个指定实例(Web应用构建器主要对基于.Net(Core)框架中的配置文件(*.json)进行读写操作,>=Net6)</param>

        /// <summary>

        /// 【配置应用设定】

        /// <remarks>

        /// 摘要:

        ///    把当前程序中所有继承了“IConfig”接口的具体实现类的实例,依赖注入到.Net(Core)内置依赖注入容器实例中,如果需要并把这些数据持久化存储到"appsettings.json"文件。

        /// </remarks>

        /// </summary>

        public static void ConfigureApplicationSettings(this IServiceCollection services, WebApplicationBuilder builder)

        {

            //有关于“ServicePointManager”见: https://learn.microsoft.com/zh-cn/dotnet/api/system.net.securityprotocoltype?view=net-7.0

            ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault;

            //获取自定义文件提供程序实例。

            CommonHelper.DefaultFileProvider = new NopFileProvider(builder.Environment);

            //获取Web应用程序域类型查找器实例。

            var typeFinder = new WebAppTypeFinder();

            //Web应用程序域类型查找器实例存储到单例实例的字典成员实例中。

            Singleton<ITypeFinder>.Instance = typeFinder;

            //Web应用程序域类型查找器实例,依赖注入到.Net(Core)内置依赖注入容器实例中。

            services.AddSingleton<ITypeFinder>(typeFinder);

            //通过反射方式把继承于“IConfig”的所有具体实现类进行实例化操作。

            var configurations = typeFinder

                .FindClassesOfType<IConfig>()

                .Select(configType => (IConfig)Activator.CreateInstance(configType)!)

                .ToList();

            //依次对继承于“IConfig”的所有具体实现类的实例,根据“appsettings.json”文件中的相关数据进行设定。

            foreach (var config in configurations)

                builder.Configuration.GetSection(config.Name).Bind(config, options => options.BindNonPublicProperties = true);

            //把当前程序中所有继承了“IConfig”接口的具体实现类的实例以键/值对的形式存储到应用配置类的的变量和属性字典成员实例中,如果需要并把这些数据持久化存储到"appsettings.json"文件。

            var appSettings = AppSettingsHelper.SaveAppSettings(configurations, CommonHelper.DefaultFileProvider, false);

            //把当前程序中所有继承了“IConfig”接口的具体实现类的实例,依赖注入到.Net(Core)内置依赖注入容器实例中。

            services.AddSingleton(appSettings);

        }

    }

}

6 重构Program.cs文件中的数据库连接依赖注入

//把当前程序中所有继承了“IConfig”接口的具体实现类的实例,依赖注入到.Net(Core)内置依赖注入容器实例中,如果需要并把这些数据持久化存储到"appsettings.json"文件。

builder.Services.ConfigureApplicationSettings(builder);

builder.Services.AddScoped<INopFileProvider, NopFileProvider>();

//从单例实例的字典成员实例中获取数据库连接相关数据。

DataConfig _dataConfigSingleton = Singleton<AppSettings>.Instance.Configuration["ConnectionStrings"].ToObject<DataConfig>()!;

//从内置依赖注入容器中例中获取数据库连接相关数据。

//注意最好不要在内置依赖注入容器中调用“builder.Services.BuildServiceProvider()”,否则会出现“ASP0000”警告信息;这里只是用“builder.Services.BuildServiceProvider()”方法来对配置数据进行调试。

DataConfig _dataConfigServiceProvider = builder.Services.BuildServiceProvider().GetService<AppSettings>().Configuration["ConnectionStrings"].ToObject<DataConfig>()!;

//说明:如果想要“EntityFrameworkCore”中间件支持多数据库软件,则把选择条件中的所有中间件都注入到依赖注入到.Net(Core)框架内置容器即可,

//选择条件来限定当前程序只支持所设定的1个数据库软件,当然“DataConfig”类与“appsettings.json”文件也必须为支持多数据库软件进行重构。

if (_dataConfigServiceProvider.DataProvider.ToString().Equals("sqlserver", StringComparison.InvariantCultureIgnoreCase))

{

    //实例化“EntityFrameworkCore”中间件只支持“SqlServer”数据库软件与当前程序进行CURD交互操作。

    //“Microsoft.EntityFrameworkCore.SqlServer”中间件实例,依赖注入到.Net(Core)框架内置容器中。

    builder.Services.AddDbContext<EFCoreContext>(

        //通过“DbContextOptionsBuilder”实例中的参数实例,为“Microsoft.EntityFrameworkCore.SqlServer”中间件的实例化提供参数实例,

        //最终把“Microsoft.EntityFrameworkCore.SqlServer”中间件实例,依赖注入到.Net(Core)框架内置容器中。

        //IIS发布部署连接字符串必须使用“SQL Server身份认证数据库连接方式,才能实现发布部署程序与数据库的CURD的操作。

        options => options.UseSqlServer(_dataConfigServiceProvider.ConnectionString));

}

else if (_dataConfigServiceProvider.DataProvider.ToString().Equals("mysql", StringComparison.InvariantCultureIgnoreCase))

{

    //实例化“EntityFrameworkCore”中间件只支持“MySql”数据库软件与当前程序进行CURD交互操作。

    //“Microsoft.EntityFrameworkCore.SqlServer”中间件和“Pomelo.EntityFrameworkCore.MySql”实例,依赖注入到.Net(Core)框架内置容器中。

    builder.Services.AddDbContext<EFCoreContext>(

    //实现“Microsoft.EntityFrameworkCore”中间件实例与“MySql”数据库的连接。

    options => options.UseMySql(_dataConfigServiceProvider.ConnectionString, MySqlServerVersion.LatestSupportedServerVersion));

}

F5执行程序不管在 “Microsoft SQL Server”数据库软件中,还是在“MySql”数据库软件中都能自动生“ShopDemo 数据库及其表。

对以上功能更为具体实现和注释见:230116_011shopDemo(配置类的反射方式实例化、单例和依赖注入)。

相关文章:

  • 郑州个人网站开发/重庆seo网站排名
  • 美国主机教育网站建设/谷歌seo软件
  • 网页传奇排行/杭州网站建设方案优化
  • 生鲜网站建设的总体目标/百度联盟怎么赚钱
  • 建设工程质量安全管理协会网站/深圳网络推广软件
  • 网站做视频转流量/贵州整站优化seo平台
  • 深度学习22- 讨论AlphaGo Zero方法的结构
  • 低代码开发前景如何?大家都真的看好低代码开发吗?
  • Chrome V3版开发教程使用Vue 3.x+Ant构建项目
  • 视频 | 生信 linux 实战题目讲解03
  • Java基础学习笔记(十六)—— IO流
  • Open3D 基于FPFH特征点的RANSAC粗配准(Python版本)
  • 本来挺喜欢刷《剑指offer》的.......(第十一天)
  • 综述 | 深度强化学习在自动驾驶中的应用
  • win10搜索大文件
  • iOS-OC实现定时器
  • diff算法-h函数-虚拟dom
  • 【运维心得】正确的校正mysql-slave及mysqldump