• .net core系列之《从源码对Configuration的底层运行机制进行分析》


    通过对Configuration源代码的分析从而来自定义一个配置数据源

    1、用反编译工具来看看AddJsonFile()这个方法究竟干了什么,源代码如下:

            public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path)
            {
                return builder.AddJsonFile(null, path, false, false);
            }
            
            public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional)
            {
                return builder.AddJsonFile(null, path, optional, false);
            }
            
            public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
            {
                return builder.AddJsonFile(null, path, optional, reloadOnChange);
            }

    首先我们可以看到这3个重载方法,它们都是调用了下面这个方法:

            public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException("builder");
                }
                if (string.IsNullOrEmpty(path))
                {
                    throw new ArgumentException(Microsoft.Extensions.Configuration.Json.Resources.Error_InvalidFilePath, "path");
                }
                return builder.AddJsonFile(delegate (JsonConfigurationSource s) {
                    s.FileProvider = provider;
                    s.Path = path;
                    s.Optional = optional;
                    s.ReloadOnChange = reloadOnChange;
                    s.ResolveFileProvider();
                });
            }

    而这个方法呢,又是调用了下面这个方法:

            public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action<JsonConfigurationSource> configureSource)
            {
                return builder.Add<JsonConfigurationSource>(configureSource);
            }

    从上面的方法可以看出,类型参数为 JsonConfigurationSource 然后返回IConfigurationBuilder的扩展方法 Add<TSource>(this IConfigurationBuilder builder, Action<TSource> configureSource)  

    代码如下:

            public static IConfigurationBuilder Add<TSource>(this IConfigurationBuilder builder, Action<TSource> configureSource) where TSource: IConfigurationSource, new()
            {
                TSource source = Activator.CreateInstance<TSource>();
                if (configureSource != null)
                {
                    configureSource(source);
                }
                return builder.Add(source);
            }

    能过这个方法,创建 JsonConfigurationSource 的一个实例,最后返回 IConfigurationBuilder  中的 Add(IConfigurationSource source) 方法,接下来我们来看看在这个方法里都干了什么吧

            public IConfigurationBuilder Add(IConfigurationSource source)
            {
                if (source == null)
                {
                    throw new ArgumentNullException("source");
                }
                this.Sources.Add(source);
                return this;
            }
    
            public IList<IConfigurationSource> Sources
            {
                [CompilerGenerated]
                get
                {
                    return this.<Sources>k__BackingField;
                }
            }

    通过以上代码我知道了,AddJsonFile()这个方法,创建了一个 JsonConfigurationSource 的一个实例,并将AddJsonFile()中的参数给了这个对象,然后将这个对象存到一个集合中。

    2、接下来我们来看看Build()这个方法干了什么:

            public IConfigurationRoot Build()
            {
                List<IConfigurationProvider> list = new List<IConfigurationProvider>();
                using (IEnumerator<IConfigurationSource> enumerator = this.Sources.GetEnumerator())
                {
                    while (enumerator.MoveNext())
                    {
                        IConfigurationProvider provider = enumerator.Current.Build(this);
                        list.Add(provider);
                    }
                }
                return new ConfigurationRoot((IList<IConfigurationProvider>) list);
            }

    这个方法通过遍历你通过AddJsonFile()方法存放进去的 IConfigurationSource 的集合,然后通过调用 IConfigurationSource 的实现类的 Build方法 ,我们来看看这个方法是怎样的:

            public override IConfigurationProvider Build(IConfigurationBuilder builder)
            {
                base.EnsureDefaults(builder);
                return new JsonConfigurationProvider(this);
            }

    这个方法返回一个 IConfigurationProvider  对象,被赋给了 List<IConfigurationProvider>  集合,接下来我们来看看 JsonConfigurationProvider 这个类里面有什么

            public override void Load(Stream stream)
            {
                try
                {
                    base.Data = JsonConfigurationFileParser.Parse(stream);
                }
                catch (JsonReaderException exception)
                {
                    string str = string.Empty;
                    if (stream.get_CanSeek())
                    {
                        stream.Seek((long) 0L, (SeekOrigin) SeekOrigin.Begin);
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            IEnumerable<string> fileContent = ReadLines(reader);
                            str = RetrieveErrorContext(exception, fileContent);
                        }
                    }
                    throw new FormatException(Microsoft.Extensions.Configuration.Json.Resources.FormatError_JSONParseError((int) exception.LineNumber, str), exception);
                }
            }

    我们看到了这里面有一个Load()方法,这个方法就是用来加载文件中的数据的。

    然后我们接着分析上面还没分析的  return new ConfigurationRoot((IList<IConfigurationProvider>) list); 这段代码

    这段代码返回了一个 ConfigurationRoot 对象,还将我们所有的 IConfigurationProvider 给传了进行,它想干嘛呢?

    没错,上面虽然有Load()方法,但是没人调用啊。 ConfigurationRoot  这个类就是干这个的。

            public ConfigurationRoot(IList<IConfigurationProvider> providers)
            {
                if (providers == null)
                {
                    throw new ArgumentNullException("providers");
                }
                this._providers = providers;
                foreach (IConfigurationProvider p in providers)
                {
                    p.Load();
                    ChangeToken.OnChange(delegate {
                        return p.GetReloadToken();
                    }, delegate {
                        this.RaiseChanged();
                    });
                }
            }

    通过以上代码的分析我们知道了Configuration的底层运行机制

    3、那么接下来我们来自定义一个获取自定义的文件的Configuration,能过以上代码我们可以分析出有几个重要的对象

      1、JsonConfigurationSource:最后从这个对象的基类的Data属性中取值

      2、JsonConfigurationProvider:这个里面的Load()方法用来加载文件数据

      3、JsonConfigurationExtensions:这个是用来定义AddJsonFile()方法的

    我们规定我们的文件后缀为.custom

    CustomConfigurationSource类代码

        public class CustomConfigurationSource : FileConfigurationSource
        {
            private string path = string.Empty;
            public CustomConfigurationSource(string path)
            {
                this.path = path;
            }
            public override IConfigurationProvider Build(IConfigurationBuilder builder)
            {
                return new CustomConfigurationProvider(this.path);
            }
        }

    CustomConfigurationProvider类代码

        public class CustomConfigurationProvider : ConfigurationProvider
        {
            private string path = string.Empty;
            public CustomConfigurationProvider(string path)
            {
                this.path = path;
            }
            public override void Load()
            {
                var lines = System.IO.File.ReadAllLines(this.path);
                var dict = new Dictionary<string, string>();
                foreach (var item in lines)
                {
                    var items = item.Split("=");
                    dict.Add(items[0], items[1]);
                }
                base.Data = dict;
            }
        }

    CustomConfigurationExtensions类代码 :

        public static class CustomConfigurationExtensions
        {
            public static IConfigurationBuilder AddCustomFile(this IConfigurationBuilder builder, string path)
            {
                return builder.Add(new CustomConfigurationSource(path));
            }
        }

    最终调用代码:

            static void Main(string[] args)
            {
                IConfiguration configuration = new ConfigurationBuilder()
                    .SetBasePath(Environment.CurrentDirectory)
                    .AddCustomFile("1.custom")
                    .Build();;
                var info = configuration["host"];
                Console.WriteLine(info);
                Console.ReadKey();
            }

    结果如下:

  • 相关阅读:
    scala-for高级用法
    scala-for
    numpy每行,没列中的最小值
    NLP
    sql-server笔记-sql
    openlayers加载geoserver的WMS服务出现点标注显示不全
    oracle删除表
    idea-debug
    python-windows安装相关问题
    mybatis-oracle 新增序列
  • 原文地址:https://www.cnblogs.com/hhzblogs/p/9703769.html
Copyright © 2020-2023  润新知