.Net 全自动绑定配置信息到配置对象

.Net 全自动绑定配置信息到配置对象

适用版本:.net core 3.1–latest

一、下面是使用一般方法绑定配置文件

这是appsettings.json 配置文件中需要我们绑定的对象数据

  "DataPoolApiOption": {
    "get_geo_model_by_idUrl": "123",
    "get_geo_prop_model_by_idUrl": "123",
    "get_sim_case_list": "123",
    "get_sim_deck_well_list": "123",
    "get_well_sim_summary_list": "123",
    "get_reservior_sim_summary_list": "123",
    "ParamFolder": "123",
    "ProtoFolder": "123"
  },

创建一个类,属性名称保证和json中一致

public class DataPoolApiOption
    {
        public string get_geo_model_by_idUrl { get; set; }
        public string get_geo_prop_model_by_idUrl { get; set; }
        public string get_sim_case_list { get; set; }
        public string get_sim_deck_well_list { get; set; }
        public string get_well_sim_summary_list { get; set; }
        public string get_reservior_sim_summary_list { get; set; }
        public string ParamFolder { get; set; }
        public string ProtoFolder { get; set; }
    }

startup.cs中我们使用Configuration.GetSection方法映射属性数据

var dpOptions = Configuration.GetSection(nameof(DataPoolApiOption));
var dpOptions = Configuration.GetSection("DataPoolApiOption");
var apiOptions = new DataPoolApiOption();
dpOptions.Bind(apiOptions);

或者 使用如下方式利用Configuration一个个的取出我们想要读取的信息
 类中构造函数赋值 private readonly IConfiguration _configuration;
var get_geo_model_by_idUrl = this._configuration["get_geo_model_by_idUrl"];

优点:简单、快。小型Demo项目完全可以使用这种方式操作,没必要搞得那么“通用”。
缺点:难以维护,哪里都是。一旦配置信息使用的地方增多,那马上就会变得毫无章法可言。每次新增,变更,删除等都要 “全局搜索” 式的修改。

二、下面我们来使用全自动注入方式进行管理
说明:利用反射、泛型、IOption等多种工具实现最优雅、解放双手式的注入管理,让我们的配置信息不在变得讨厌。
背景:参考了全局注入代码,由此演变而来的自动注入方式

优点:映射关系清晰,管理维护方便,统一修改统一更新,不担心滥用,只负责管理对象和json即可

第一步:首先我们定义一个Interface,该Interface的目的是给我们的被Bind类打上记号,告诉反射我们要映射的对象是谁,让我们需要bind的类继承这个interface

    /// <summary>
    /// 该接口需要所有配置绑定对象实现(继承该接口系统自动识别并注入配置对象)
    /// </summary>
    public interface IAutoOptoin
    {
    }

使用上面定义好的类继承 IAutoOption
使用上面定义好的类继承 IAutoOption
第二步:编写一般的绑定方法,如下

         /// <summary>
    /// 注入工具
    /// </summary>
    public class InjectInOptions
    {
        /// <summary>
        /// 向Options中注入 - 注入方法
        /// 目前被反射 AutoInjectOptionsExtend.AutoInjectOptions<T> 调用
        /// </summary>
        /// <typeparam name="T">被注入类型</typeparam>
        /// <param name="services">被扩展的服务</param>
        /// <param name="configuration">程序配置</param>
        /// <param name="key">appsetting key</param>
        public static void SetInOptionProvider<T>(IServiceCollection services, IConfiguration configuration, string key) where T : class
        {
            services.Configure<T>(configuration.GetSection(key));
        }
    }

第三步:写一个反射调用静态泛型方法的功能,如下 (参数代码中有说明,看不太明白的小伙伴可以拿下来试试)

/// <summary>
    /// 反射自动识别配置类
    /// </summary>
    public class InvokeMethodUtil
    {
        /// <summary>
        /// 反射调用泛型方法
        /// </summary>
        /// <param name="type">方法所在类</param>
        /// <param name="genericTypes">泛型type[]</param>
        /// <param name="methodName">被调用方法名称</param>
        /// <param name="methodParams">被调用方法所有参数</param>
        public static void BeginInvokeGenericMethod(Type type, Type[] genericTypes, string methodName, params object[] methodParams)
        {
            创建实例
            var ins = Activator.CreateInstance(type);
            获取方法堆栈
            MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static) ?? throw new ArgumentException($"Reflection failed{JsonConvert.SerializeObject(ins)}");
            获取泛型方法堆栈
            method = method.MakeGenericMethod(genericTypes);
            执行泛型结果
            method.Invoke(ins, methodParams);
        }
    }

现在一切准备就绪,我们再编写一个过滤全局被绑定的类、同时拼接以上三步的静态扩展方法就好了

第四步:使用RuntimeUtil内置的获取运行程序所有的类的思路,过滤出我们想要绑定的对象,同时利用反射动态抓取配置文件的配置对象进行绑定

》》》》前提条件:被绑定的类名一定要和appsetting.json中的对象名一致,否则会出现无法成功绑定数据的情况,但不会报错《《《《

RuntimeUtil我会放在下面

 /// <summary>
    /// 自动化工具
    /// </summary>
    public static class AutoInjectOptionsExtend
    {
        /// <summary>
        /// 根据接口自动注入实现对象
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        /// <param name="configuration">IConfiguration</param>
        public static void AutoInjectOptions<T>(this IServiceCollection services, IConfiguration configuration)
        {
            foreach (var item in RuntimeUtil.GetAllCoreAssemblies())
            {
                foreach (var install in item.GetTypes().Where(t => typeof(T).IsAssignableFrom(t) && t.IsClass))
                {
                    Type[] invokeTypes = new Type[1] { install };
                    object[] invokeParams = new object[3] { services, configuration, install.Name };
                    InvokeMethodUtil.BeginInvokeGenericMethod(typeof(InjectInOptions), invokeTypes, nameof(InjectInOptions.SetInOptionProvider), invokeParams);
                };
            }
        }
    }

该方法中的泛型类型就是在上面定义的IAutoOption,使用方法如下:
startup.cs中

public void ConfigureServices(IServiceCollection services)
        {
			services.AutoInjectOptions<IAutoOptoin>(Configuration);
		}

效果展示

1、通过构造函数注入 .Value取出绑定数据后的对象
loyar
2、测试方法
loyar
3、结果 : 可以看到和我们的appsettings.json中数据是一致的
在这里插入图片描述
在这里插入图片描述

如果改进和不足请同行大佬及时指出,我们互相学习进步!
By Loyar.

RuntimeUtil :
依赖包:Microsoft.Extensions.DependencyModel

    /// <summary>
    /// 运行时扩展工具
    /// </summary>
    public class RuntimeUtil
    {
        /// <summary>
        /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包
        /// </summary>
        /// <returns></returns>
        public static IList<Assembly> GetAllAssemblies()
        {
            var list = new List<Assembly>();
            var deps = DependencyContext.Default ?? throw new ArgumentException("注入内容获取失败 位置:RuntimeUtil.GetAllAssemblies() 18-Line");
            //排除所有的系统程序集、Nuget下载包
            var libs = deps.CompileLibraries.Where(lib => lib.Type == AssembleTypeConsts.Project);//只获取本项目用到的包
            foreach (var lib in libs)
            {
                try
                {
                    var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
                    list.Add(assembly);
                }
                catch (System.Exception ex)
                {
                    throw new System.Exception(ex.ToString());
                }
            }
            return list;
        }

        /// <summary>
        /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包和Yuebon.Commons.dll
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Assembly> GetAllCoreAssemblies()
        {
            var list = new List<Assembly>();
            var deps = DependencyContext.Default ?? throw new ArgumentNullException("获取注入内容出错 位置 :RuntimeUtil.GetAllCoreAssemblies() 41-Line ");
            //排除所有的系统程序集、Nuget下载包
            var libs = deps.CompileLibraries.Where(lib => lib.Type == AssembleTypeConsts.Project);//只获取本项目用到的包
            foreach (var lib in libs)
            {
                try
                {
                    var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
                    list.Add(assembly);
                }
                catch (System.Exception ex)
                {
                    throw new System.Exception(ex.ToString());
                }
            }
            return list;
        }

        /// <summary>
        ///获取某个程序集下所有信息
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <returns></returns>
        public static Assembly GetAssembly(string assemblyName)
        {
            return GetAllCoreAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName));
        }

        /// <summary>
        ///获取所有Type属性
        /// </summary>
        /// <returns></returns>
        public static IList<Type> GetAllTypes()
        {
            var list = new List<Type>();
            foreach (var assembly in GetAllAssemblies())
            {
                var typeInfos = assembly.DefinedTypes;
                foreach (var typeInfo in typeInfos)
                {
                    list.Add(typeInfo.AsType());
                }
            }

            return list;
        }

        /// <summary>
        ///根据程序集获取Type属性
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <returns></returns>
        public static IList<Type> GetTypesByAssembly(string assemblyName)
        {
            var list = new List<Type>();
            var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName));
            var typeInfos = assembly.DefinedTypes;

            foreach (var typeInfo in typeInfos)
            {
                list.Add(typeInfo.AsType());
            }

            return list;
        }

        /// <summary>
        /// 获取实现类
        /// </summary>
        /// <param name="typeName"></param>
        /// <param name="baseInterfaceType"></param>
        /// <returns></returns>
        public static Type? GetImplementType(string typeName, Type baseInterfaceType)
        {
            return GetAllTypes().FirstOrDefault(t =>
            {
                if (t.Name == typeName &&
                    t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name))
                {
                    var typeInfo = t.GetTypeInfo();
                    return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType;
                }

                return false;
            });
        }
    }

    /// <summary>
    ///基本配置
    /// </summary>
    public class AssembleTypeConsts
    {
        /// <summary>
        ///依赖包
        /// </summary>
        public const string Package = "package";

        /// <summary>
        /// 程序集
        /// </summary>
        public const string ReferenceAssembly = "referenceassembly";

        /// <summary>
        /// 项目名称
        /// </summary>
        public const string Project = "project";
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值