Targo-MicroService:独立的ConfigService设计(一)

Targo.Net独立的ConfigService自动注册设计



提示:这一篇可能有点饶但是结合我画的图和源代码来理解还是很简单的

前言

这样设计的目的,在之后的组件封装的过程中,我们不需要再去到host去管理所有组件的服务配置了,使用这个初始化组件,等于添加了一个自动发现IServiceCollection并自动执行的工具,使用递归算法对项目的引用程序集进行扫描,当存在我们指定内容的程序集时,自动进行调用。

前提条件:

  1. 添加appsetting.json中InitializationAssemblyHead,例如 “InitializationAssemblyHead”: “Targo|targo|kenny”,
  2. 在我们的组件中,添加一个IServiceCollection的扩展方法(程序集会依据IServiceCollection扩展方法查找要执行的方法)进行自动调用执行。

涉及到的内容

  1. Assembly
  2. Type
  3. Methods
  4. 反射

大图走一波(初始化应用程序)

提示:这大图画的比较紧凑,大概意思可以表达一下就可以了
在这里插入图片描述


下面是Assembly,Type,Methods的本篇常用方法,所有方法太多了,想要了解的小伙伴可以逛逛官网了解下。

反射操作()

概念

程序集构成了 .NET 应用程序的部署、版本控制、重用、激活范围和安全权限的基本单元,是为协同工作而生成的类型和资源的集合,这些类型和资源构成了一个逻辑功能单元,采用可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式,是 .NET 应用程序的构建基块。 它们向公共语言运行时提供了注意类型实现代码所需的信息。

特点

  1. 以 .exe 或 .dll 文件的形式实现 。
  2. 只有在需要使用时才会将程序集加载到内存中。
  3. 可以使用反射,以编程方式获取程序集的相关信息。
  4. 可加载一个程序集,使用 .NET 和 .NET Framework 中的 MetadataLoadContext 类来检查该程序集。

本篇常用方法

  • AppDomain.CurrentDomain.GetAssemblies():获取当前应用领域的程序集。
  • GetReferencedAssemblies():获取程序引用程序集。
  • Assembly.Load(assemblyName):根基assemblyName加载程序集。
  • GetType():获取类型。
  • GetTypes():获取类型数组。
    仅当引用项目并且在代码中有使用时,才会将程序集加载到内存中,并通过GetAssemblies()获取到,如果只是引用了程序集,并 没有引用则需要通过GetReferencedAssemblies()来获取程序集名称,且获取到的只为当前项目直接引用的程序集,如果想获得多层次的指定引用程序集,则需要使用递归算法,来加载所有指定的程序集。

更多关于Assembly,Type和Method反射操作相关可以查看微软官网,这里就不详细说明了。

Targo.Net说明

主体设计说明

  • V 0.0.1 从Program 直接进行配置Service注册服务。
  • V 0.0.2 通过将注册配置拆分到具体的子类库中,由program统一进行调用注册。
  • V 0.0.3 通过添加统一的注册服务,使用attribute标识进行过滤注册。
  • V 0.0.4 修改attribute标识,改为IServiceCollection扩展方法及appsetting中设置的程序集头部名称进行过滤,获取到需要执行的方法,通过反射进行自动调用,将服务进行注册。

实现

  1. 在appsetting.json文件中添加配置项InitializationAssemblyHead,用来标识需要执行自动注册的类库头部,自动注册将会对此字符串进行解析,过滤出带有这些标识的Lib在这些类中,查找IServiceCollection 的扩展方法,然后统一进行服务注册。例如:“Targo|targo|kenny”。以“|”进行分隔。
  2. 通过引入Targo.Initialization.App 类库,添加初始化服务注册–builder.Services.InitService();
  3. 通过递归获取到当前项目所有引用的指定InitializationAssemblyHead的程序集,并筛选出IServiceCollection的扩展方法,通过反射进行调用即可触发项目所有的ConfigurationService方法,

源码

第一步:配置Appsetting.json

"InitializationAssemblyHead": "Targo|targo|kenny",

第二步:添加项目初始化服务

builder.Services.InitService();//初始化服务注册

第三步:InitializationAppExtention初始化程序扩展

/// <summary>
    /// 初始程序扩展
    /// </summary>
    public static class InitializationAppExtention
    {
        /// <summary>
        /// 初始化服务,默认从IServiceCollection 中buildServiceProvider,获取IConfiguration
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection InitService(this IServiceCollection services)
        {
            var serviceProvider = services.BuildServiceProvider();
            var configuration = (IConfiguration)serviceProvider.GetServices(typeof(IConfiguration)).First();
            InitService(services, configuration);
            return services;
        }
        /// <summary>
        /// 初始化注册服务
        /// </summary>
        /// <param name="services">服务扩展对象</param>
        /// <param name="configuration">配置文件对象</param>
        /// <returns></returns>
        public static IServiceCollection InitService(this IServiceCollection services, IConfiguration configuration)
        {
            try
            {   
                InitialzationAppProvider.InitializeConfigService(services, configuration).GetAwaiter().GetResult();
                return services;
            }
            catch (Exception error)
            {
                Console.WriteLine(error.Message);
                return services;
            }
        }
    }

第四步:InitialzationAppProvider 初始化程序服务提供者

  /// <summary>
    /// 提供初始化操作
    /// </summary>
    internal static class InitialzationAppProvider
    {
        /// <summary>
        /// 配置文件的程序集头
        /// 以|来进行区分
        /// </summary>
        private static string[] InitializationAssemblyHead { get; set; }

        /// <summary>
        /// 初始化配置服务
        /// </summary>
        /// <param name="service"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static async Task<IServiceCollection> InitializeConfigService(IServiceCollection service, IConfiguration configuration)
        {

            InitializationAssemblyHead = configuration.GetSection("InitializationAssemblyHead").Value.Split('|');
            var CurreryDomainAssemblies = await FindAllReferenceAssemblies();

            var curreryDomainGetMethods = CurreryDomainAssemblies
                .Where(p => !p.FullName.Equals(Assembly.GetExecutingAssembly().FullName)).ToList()//先过滤一下当前的程序集,不能重复跑了
                .SelectMany(p => p.GetTypes().SelectMany(x => x.GetMethods()))//设置查询对象为methods
                .Where(p => p.IsStatic.Equals(true) && p.IsDefined(typeof(ExtensionAttribute)))//筛选方法为静态扩展方法                
                .Where(p => p.GetParameters()[0].ParameterType.Equals(typeof(IServiceCollection))) //获取为Service注入方法,指定第一个参数是IServiceCollection
                .ToList();

            curreryDomainGetMethods.ForEach(item =>
            {
                Console.WriteLine(item.Name);
                Console.WriteLine("GetType FullName" + item.GetType().FullName);
                Type currItemType = item.GetType();

                if (item.GetParameters().Count() > 1)
                {                    
                    item.Invoke(null, new object[] { service, configuration });
                }
                else
                {
                    item.Invoke(null, new object[] { service });
                }
            });
            return service;
        }
        /// <summary>
        /// 获取所有的程序集
        /// </summary>
        /// <param name="service"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        private static async Task<List<Assembly>> FindAllReferenceAssemblies()
        {
            var ReferencedAssemblies = AppDomain.CurrentDomain.GetAssemblies().SelectMany(p => p.GetReferencedAssemblies()).ToArray();
            var ReferencedAssembliesParams = GetIEnumerableQueryAsync(ReferencedAssemblies).Result.ToArray();
            return RecurrenceAssemblyAsync(ReferencedAssembliesParams).Result.ToList();//去递归获取包含指定内容的程序集
        }

        /// <summary>
        /// 根据assemblyName集合获取assembly集合
        /// </summary>
        /// <param name="assemblyNameArray"></param>
        /// <returns></returns>
        private static async Task<List<Assembly>> RecurrenceAssemblyAsync(AssemblyName[] assemblyNameArray)
        {
            var resultlist = new List<Assembly>();
            var assemblyList = await LoadAssemblysByNameArrayAsync(assemblyNameArray);
            foreach (var item in assemblyList)
            {
                if (resultlist.Contains(item))
                {
                    continue;
                }
                else
                {
                    resultlist.Add(item);
                }
            }

            //遍历程序集获取集合
            foreach (var item in assemblyList)
            {
                //根据assemblyname获取所有的assembly
                if (item.HasSubReferenceAsync().Result)
                {
                    //当前item存在需要的子程序集

                    var RecurrenceAssemblyAsyncParams = GetIEnumerableQueryAsync(item.GetReferencedAssemblies()).Result.ToArray();
                    //RecurrenceAssemblyAsyncParams.ToList().ForEach(p => Console.WriteLine(p.FullName));
                    var list = RecurrenceAssemblyAsync(RecurrenceAssemblyAsyncParams.ToArray()).Result.ToList();
                    if (list.Equals(null) && list.Count == 0)
                    {
                        continue;
                    }
                    else
                    {

                        foreach (var itemlist in list)
                        {
                            if (resultlist.Contains(itemlist))
                            {
                                continue;
                            }
                            else
                            {
                                resultlist.Add(itemlist);
                            }
                        }
                    }
                }
                else
                {
                    continue;
                }
            }
            return resultlist;
        }


        /// <summary>
        /// 根据assemblyName的数组加载程序集到list,并返回
        /// </summary>
        /// <param name="assemblyNames"></param>
        /// <returns></returns>
        private static async Task<List<Assembly>> LoadAssemblysByNameArrayAsync(AssemblyName[] array)
        {
            var list = new List<Assembly>();
            foreach (var item in array)
            {
                list.Add(Assembly.Load(item));
            }
            return list;
        }
        /// <summary>
        /// 判断当前程序集否存在我们要筛选的指定程序集名称
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        private static async Task<bool> HasSubReferenceAsync(this Assembly assembly)
        {
            var result = await GetIEnumerableQueryAsync(assembly.GetReferencedAssemblies());
            //.ToList();
            var list = result.ToList();
            if (list is null || list.Count == 0)
            {
                return await Task.FromResult(false);
            }
            else
            {
                return await Task.FromResult(true);
            }
        }

        /// <summary>
        /// 内部调用获取动态筛选条件
        /// </summary>
        /// <param name="allAssembliesName"></param>
        /// <returns>IEnumerable</returns>

        private static async Task<IEnumerable<AssemblyName>> GetIEnumerableQueryAsync(AssemblyName[] allAssembliesName)
        {
            IEnumerable<AssemblyName> allquery = null;
            foreach (var item in InitializationAssemblyHead)
            {
                if (Array.IndexOf(InitializationAssemblyHead, item).Equals(0))
                {
                    allquery = from a1 in allAssembliesName
                               where a1.Name.Contains(item)
                               select a1;
                }
                else
                {
                    allquery = allquery.Union(from a1 in allAssembliesName
                                              where a1.Name.Contains(item)
                                              select a1);
                }
            }
            return allquery;
        }

    }

Targo-MicroService-----GitHub


下篇预告

这篇先从整体确定了组件的服务注册框架,下篇会对具体结构的业务服务注册进行说明,业务服务注册也是基于反射进行实现的,本篇使用的外层调用各个子lib的注入方法实现,下篇就是子lib内部服务注册具体的操作了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kenny@chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值