diff --git a/Apollo.AspNetCoreHosting/Apollo.AspNetCoreHosting.csproj b/Apollo.AspNetCoreHosting/Apollo.AspNetCoreHosting.csproj index 0d2e389..e0a8303 100644 --- a/Apollo.AspNetCoreHosting/Apollo.AspNetCoreHosting.csproj +++ b/Apollo.AspNetCoreHosting/Apollo.AspNetCoreHosting.csproj @@ -15,7 +15,7 @@ $(PackageReleaseNotes) $(RepositoryUrl)/$(MSBuildProjectName) Microsoft.AspNetCore.Hosting netstandard2.0 - $(ApolloVersion) + $(ApolloVersion).0 diff --git a/Apollo.ConfigAdapter.Yaml/Apollo.ConfigAdapter.Yaml.csproj b/Apollo.ConfigAdapter.Yaml/Apollo.ConfigAdapter.Yaml.csproj index 656031e..d480fde 100644 --- a/Apollo.ConfigAdapter.Yaml/Apollo.ConfigAdapter.Yaml.csproj +++ b/Apollo.ConfigAdapter.Yaml/Apollo.ConfigAdapter.Yaml.csproj @@ -11,7 +11,7 @@ $(PackageReleaseNotes) $(PackageTags) yml yaml Com.Ctrip.Framework.Apollo.ConfigAdapter net40;net45;netstandard2.0 - $(ApolloVersion) + $(ApolloVersion).0 diff --git a/Apollo.Configuration/Apollo.Configuration.csproj b/Apollo.Configuration/Apollo.Configuration.csproj index 197abc7..f84fdbe 100644 --- a/Apollo.Configuration/Apollo.Configuration.csproj +++ b/Apollo.Configuration/Apollo.Configuration.csproj @@ -32,7 +32,7 @@ $(PackageReleaseNotes) $(RepositoryUrl)/$(MSBuildProjectName) Com.Ctrip.Framework.Apollo netstandard2.0 - $(ApolloVersion) + $(ApolloVersion).0 diff --git a/Apollo.ConfigurationManager.Tests/App.config b/Apollo.ConfigurationManager.Tests/App.config index 6462a34..cbb5a98 100644 --- a/Apollo.ConfigurationManager.Tests/App.config +++ b/Apollo.ConfigurationManager.Tests/App.config @@ -3,22 +3,34 @@
+
+ + + + + + + + + + + diff --git a/Apollo.ConfigurationManager.Tests/ConfigExtensionsTest.cs b/Apollo.ConfigurationManager.Tests/ConfigExtensionsTest.cs new file mode 100644 index 0000000..639c5bb --- /dev/null +++ b/Apollo.ConfigurationManager.Tests/ConfigExtensionsTest.cs @@ -0,0 +1,88 @@ +using Com.Ctrip.Framework.Apollo; +using Xunit; + +namespace Apollo.ConfigurationManager.Tests; + +public class ConfigExtensionsTest +{ + [Fact] + public void GetChildren() + { + var config = new TestConfig(Create("a:B", "1"), Create("A:c", "2"), Create("A:C:d", "4"), Create("b", "3")); + + Assert.Equal(new[] { "a", "b" }, config.GetChildren("").Select(x => x.Name)); + Assert.Equal(new[] { "B", "c" }, config.GetChildren("a").Select(x => x.Name)); + Assert.Equal(new[] { "a:B", "A:c" }, config.GetChildren("a").Select(x => x.FullName)); + } + + [Fact] + public void GetConnectionStrings_ConnectionString_Prior() + { + var config = new TestConfig(Create("a:b", "123"), Create("a:b:ConnectionString", "abc")); + + var connectionString = config.GetConnectionStrings("a", "sql").Single(); + + Assert.Equal("b", connectionString.Name); + Assert.Equal("abc", connectionString.ConnectionString); + Assert.Equal("sql", connectionString.ProviderName); + } + + [Fact] + public void GetConnectionStrings_ProviderName() + { + var config = new TestConfig(Create("a:b", "123"), Create("a:b:ProviderName", "abc")); + + var connectionString = config.GetConnectionStrings("a", "sql").Single(); + + Assert.Equal("123", connectionString.ConnectionString); + Assert.Equal("abc", connectionString.ProviderName); + } + + [Fact] + public void GetConnectionStrings_No_Prefix() + { + var config = new TestConfig(Create("a", "123")); + + var connectionString = config.GetConnectionStrings("", "sql").Single(); + + Assert.Equal("a", connectionString.Name); + Assert.Equal("123", connectionString.ConnectionString); + } + + [Fact] + public void WithPrefix() + { + IConfig config = new TestConfig(Create("a:B", "1"), Create("A:c", "2"), Create("A:C:d", "4"), Create("b", "3")); + + Assert.Same(config, config.WithPrefix(" ")); + + config = config.WithPrefix("a"); + + Assert.Equal(new[] { "B", "c", "C:d" }, config.GetPropertyNames()); + Assert.Equal(new[] { "1", "2", "4" }, config.GetPropertyNames().Select(p => config.GetProperty(p, ""))); + } + + private static KeyValuePair Create(string key, string value) => new(key, value); + + private class TestConfig : IConfig + { + private readonly IReadOnlyDictionary _dict; + + public TestConfig(params KeyValuePair[] keyValues) + { + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var kv in keyValues) dict[kv.Key] = kv.Value; + + _dict = dict; + } + + public bool TryGetProperty(string key, [NotNullWhen(true)] out string? value) => + _dict.TryGetValue(key, out value); + + public IEnumerable GetPropertyNames() => _dict.Keys; + + public event ConfigChangeEvent ConfigChanged = default!; + } +} + diff --git a/Apollo.ConfigurationManager.Tests/ConfigurationBuilderTest.cs b/Apollo.ConfigurationManager.Tests/ConfigurationBuilderTest.cs index 5da7ebb..8014765 100644 --- a/Apollo.ConfigurationManager.Tests/ConfigurationBuilderTest.cs +++ b/Apollo.ConfigurationManager.Tests/ConfigurationBuilderTest.cs @@ -8,7 +8,7 @@ public class ConfigurationBuilderTest #if NET471_OR_GREATER [Fact] public void AppSettingsSectionBuilderTest() => - Assert.Equal("560", System.Configuration.ConfigurationManager.AppSettings["Timeout"]); + Assert.Equal("00:00:30", System.Configuration.ConfigurationManager.AppSettings["Timeout"]); [Fact] public void ConnectionStringsSectionBuilderTest() => @@ -26,5 +26,40 @@ public void NodeReplaceSectionBuilderTest() Assert.Equal(new Uri("http://localhost:1234"), endpoint.Address); Assert.Equal("test", endpoint.Name); } + + [Fact] + public void CommonConfigurationBuilderTest() + { + var test = (TestConfigurationSection)System.Configuration.ConfigurationManager.GetSection("test"); + + Assert.Equal(3, test.DefaultValue); + + Assert.Equal(TimeSpan.FromSeconds(30), test.Timeout); + + Assert.Equal(100, test.MaxValue); + + Assert.NotNull(test.Map); + + var element = test.Map["abc"]; + + Assert.NotNull(element); + + Assert.Equal("abc", element.Key); + Assert.Equal("123", element.Value); + + Assert.Null(test.Map["def"]); + + element = test.Map["defg"]; + + Assert.NotNull(element); + + Assert.Equal("defg", element.Key); + Assert.Equal("456", element.Value); + + Assert.NotNull(test.Element); + + Assert.Equal("jkl", test.Element.Name); + Assert.Equal("789", test.Element.Value); + } #endif -} \ No newline at end of file +} diff --git a/Apollo.ConfigurationManager.Tests/GetChildrenTest.cs b/Apollo.ConfigurationManager.Tests/GetChildrenTest.cs deleted file mode 100644 index f42bb11..0000000 --- a/Apollo.ConfigurationManager.Tests/GetChildrenTest.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Com.Ctrip.Framework.Apollo; -using Xunit; - -namespace Apollo.ConfigurationManager.Tests; - -public class GetChildrenTest -{ - [Fact] - public void Test() - { - var config = new FakeConfig(new Dictionary - { - { "a:B", "1" }, - { "A:c", "2" }, - { "A:C:d", "4" }, - }); - - Assert.Equal(new[] { "B", "c" }, config.GetChildren("a").Select(x => x.Name)); - Assert.Equal(new[] { "a:B", "A:c" }, config.GetChildren("a").Select(x => x.FullName)); - Assert.Equal(new[] { "d" }, config.GetChildren("a:C").Select(x => x.Name)); - } - - private class FakeConfig : IConfig - { - private readonly IReadOnlyDictionary _data; - - public event ConfigChangeEvent ConfigChanged = default!; - - public FakeConfig(IReadOnlyDictionary data) => _data = data; - - public IEnumerable GetPropertyNames() => _data.Keys; - - public bool TryGetProperty(string key, [NotNullWhen(true)] out string? value) => _data.TryGetValue(key, out value); - } -} \ No newline at end of file diff --git a/Apollo.ConfigurationManager.Tests/TestConfigurationSection.cs b/Apollo.ConfigurationManager.Tests/TestConfigurationSection.cs new file mode 100644 index 0000000..2cf5238 --- /dev/null +++ b/Apollo.ConfigurationManager.Tests/TestConfigurationSection.cs @@ -0,0 +1,21 @@ +using System.Configuration; + +namespace Apollo.ConfigurationManager.Tests; + +public class TestConfigurationSection : ConfigurationSection +{ + [ConfigurationProperty("timeout")] + public TimeSpan Timeout => (TimeSpan)this["timeout"]; + + [ConfigurationProperty("maxValue")] + public int MaxValue => (int)this["maxValue"]; + + [ConfigurationProperty("defaultValue", DefaultValue = 3L)] + public long DefaultValue => (long)this["defaultValue"]; + + [ConfigurationProperty("element")] + public NameValueConfigurationElement Element => (NameValueConfigurationElement)this["element"]; + + [ConfigurationProperty("map")] + public KeyValueConfigurationCollection Map => (KeyValueConfigurationCollection)this["map"]; +} diff --git a/Apollo.ConfigurationManager.Tests/XmlExtensionsTest.cs b/Apollo.ConfigurationManager.Tests/XmlExtensionsTest.cs deleted file mode 100644 index 3686095..0000000 --- a/Apollo.ConfigurationManager.Tests/XmlExtensionsTest.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Com.Ctrip.Framework.Apollo; -using Com.Ctrip.Framework.Apollo.ConfigAdapter; -using Com.Ctrip.Framework.Apollo.Core.Utils; -using System.Xml; -using Xunit; - -namespace Apollo.ConfigurationManager.Tests; - -public class XmlExtensionsTest -{ -#if NET471_OR_GREATER - [Theory] - [InlineData("def", - "", - "def")] - public void BindTest(string originalXml, string newXml, string resultXml) - { - var doc = new XmlDocument(); - - doc.LoadXml(originalXml); - - XmlNode? node = doc.DocumentElement; - - node?.Bind(new FakeConfig(new XmlConfigAdapter().GetProperties($"{newXml}")), node.Name); - - Assert.Equal(resultXml, node?.OuterXml); - } -#endif - private class FakeConfig : IConfig - { - private readonly Properties _data; - - public event ConfigChangeEvent ConfigChanged = default!; - - public FakeConfig(Properties data) => _data = data; - - public IEnumerable GetPropertyNames() => _data.GetPropertyNames(); - - public bool TryGetProperty(string key, [NotNullWhen(true)] out string? value) => _data.TryGetProperty(key, out value); - } -} \ No newline at end of file diff --git a/Apollo.ConfigurationManager/Apollo.ConfigurationManager.csproj b/Apollo.ConfigurationManager/Apollo.ConfigurationManager.csproj index 9e7380d..ef2bf44 100644 --- a/Apollo.ConfigurationManager/Apollo.ConfigurationManager.csproj +++ b/Apollo.ConfigurationManager/Apollo.ConfigurationManager.csproj @@ -11,12 +11,11 @@   直接使用ApolloConfigurationManager请使用Com.Ctrip.Framework.Apollo.Configuration或者Com.Ctrip.Framework.Apollo.ConfigurationManager $(PackageReleaseNotes) - https://raw.githubusercontent.com/apolloconfig/apollo/master/apollo-portal/src/main/resources/static/img/config.png $(RepositoryUrl)/$(MSBuildProjectName) $(PackageTags) ConfigurationBuilder ConfigurationManager Com.Ctrip.Framework.Apollo net471;net45;net40;netstandard2.0;netstandard2.1 - $(ApolloVersion) + $(ApolloVersion).1 @@ -36,7 +35,6 @@ $(PackageReleaseNotes) - diff --git a/Apollo.ConfigurationManager/AppSettingsSectionBuilder.cs b/Apollo.ConfigurationManager/AppSettingsSectionBuilder.cs index 70493a9..272c5e6 100644 --- a/Apollo.ConfigurationManager/AppSettingsSectionBuilder.cs +++ b/Apollo.ConfigurationManager/AppSettingsSectionBuilder.cs @@ -5,6 +5,18 @@ namespace Com.Ctrip.Framework.Apollo; public class AppSettingsSectionBuilder : ApolloConfigurationBuilder { + private string? _keyPrefix; + + public override void Initialize(string name, NameValueCollection config) + { + base.Initialize(name, config); + + _keyPrefix = config["keyPrefix"]; + + if (!string.IsNullOrWhiteSpace(_keyPrefix) && !_keyPrefix.EndsWith(":")) + _keyPrefix += ":"; + } + public override ConfigurationSection ProcessConfigurationSection(ConfigurationSection configSection) { if (configSection is not AppSettingsSection section) return base.ProcessConfigurationSection(configSection); @@ -15,7 +27,8 @@ public override ConfigurationSection ProcessConfigurationSection(ConfigurationSe lock (this) { - var config = GetConfig(); + var config = GetConfig().WithPrefix(_keyPrefix); + foreach (var key in config.GetPropertyNames()) { if (config.TryGetProperty(key, out var value)) @@ -39,4 +52,4 @@ private static void TrySetConfigUtil(KeyValueConfigurationCollection appSettings ConfigUtil.AppSettings = settings; } -} \ No newline at end of file +} diff --git a/Apollo.ConfigurationManager/CommonSectionBuilder.cs b/Apollo.ConfigurationManager/CommonSectionBuilder.cs new file mode 100644 index 0000000..6a05131 --- /dev/null +++ b/Apollo.ConfigurationManager/CommonSectionBuilder.cs @@ -0,0 +1,175 @@ +using System.Configuration; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.ExceptionServices; +using static Com.Ctrip.Framework.Apollo.ConfigExtensions; + +namespace Com.Ctrip.Framework.Apollo; + +public class CommonSectionBuilder : ApolloConfigurationBuilder +{ + private static readonly Action Add; + private static readonly Func CreateNewElement; + private static readonly Action SetValue; + private string? _keyPrefix; + + static CommonSectionBuilder() + { + var set = typeof(ConfigurationElement).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .First(p => + { + var ip = p.GetIndexParameters(); + + return ip.Length == 1 && ip[0].ParameterType == typeof(ConfigurationProperty); + }) + .SetMethod; + + var convertFromString = typeof(ConfigurationProperty).GetMethod("ConvertFromString", BindingFlags.Instance | BindingFlags.NonPublic)!; + + var createNewElement = typeof(ConfigurationElementCollection).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .First(m => m.Name == nameof(CreateNewElement) && m.GetParameters().Length == 0); + + var param0 = Expression.Parameter(typeof(ConfigurationElementCollection)); + var param1 = Expression.Parameter(typeof(ConfigurationElement)); + var param2 = Expression.Parameter(typeof(ConfigurationProperty)); + var param3 = Expression.Parameter(typeof(string)); + + Add = CreateAddMethod(); + + CreateNewElement = Expression.Lambda>( + Expression.Call(param0, createNewElement), param0).Compile(); + + SetValue = Expression.Lambda>( + Expression.Call(param1, set, param2, Expression.Call(param2, convertFromString, param3)), + param1, param2, param3).Compile(); + } + + public override void Initialize(string name, NameValueCollection config) + { + base.Initialize(name, config); + + _keyPrefix = config["keyPrefix"]?.TrimEnd(':'); + } + + public override ConfigurationSection ProcessConfigurationSection(ConfigurationSection configSection) + { + Bind(configSection, GetConfig(), string.IsNullOrWhiteSpace(_keyPrefix ??= configSection.SectionInformation.SectionName) + ? new ConfigKey("", "") + : new ConfigKey(_keyPrefix!.Substring(_keyPrefix!.LastIndexOf(':') + 1), _keyPrefix)); + + return configSection; + } + + private static void Bind(ConfigurationElement configElement, IConfig config, ConfigKey configKey) + { + if (string.IsNullOrWhiteSpace(configKey.FullName) && + configElement is not ConfigurationSection) return; + + foreach (var property in configElement.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) + { + var cpa = property.GetCustomAttribute(); + if (cpa == null) continue; + + var key = string.IsNullOrWhiteSpace(configKey.FullName) ? cpa.Name : $"{configKey.FullName}:{cpa.Name}"; + + if (typeof(ConfigurationElement).IsAssignableFrom(property.PropertyType)) + { + var element = property.GetValue(configElement); + if (element == null) property.SetValue(configElement, element = Activator.CreateInstance(property.PropertyType)); + + if (element is ConfigurationElementCollection cec) + { + foreach (var child in config.GetChildren(key)) + { + var ele = CreateNewElement(cec); + + Bind(ele, config, child); + + Add(cec, ele); + } + } + else if (element is ConfigurationElement ce) Bind(ce, config, new ConfigKey(cpa.Name, key)); + } + else + { + var cp = new ConfigurationProperty(cpa.Name, property.PropertyType, cpa.DefaultValue, cpa.Options); + + ExceptionDispatchInfo? ex = null; + + if (cpa.IsKey && !string.IsNullOrWhiteSpace(configKey.Name)) + try + { + SetValue(configElement, cp, configKey.Name); + } + catch (ConfigurationErrorsException e) + { + ex = ExceptionDispatchInfo.Capture(e); + } + else if (string.Equals("value", cpa.Name, StringComparison.OrdinalIgnoreCase) && + !string.IsNullOrWhiteSpace(configKey.FullName) && + config.TryGetProperty(configKey.FullName, out var value)) + try + { + SetValue(configElement, cp, value); + } + catch (ConfigurationErrorsException e) + { + ex = ExceptionDispatchInfo.Capture(e); + } + + if (ex == null) + { + if (config.TryGetProperty(key, out var value)) + SetValue(configElement, cp, value); + } + else if (config.TryGetProperty(key, out var value)) + SetValue(configElement, cp, value); + else ex.Throw(); + } + } + } + + private static Action CreateAddMethod() + { + var methods = typeof(ConfigurationElementCollection) + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + + MethodInfo GetMethod(string method, params Type[] parameterTypes) + { + var ms = methods.Where(m => m.Name == method).ToArray(); + if (parameterTypes.Length < 1) return ms.FirstOrDefault(m => m.GetParameters().Length == 0) ?? ms.First(); + + return ms.First(m => + { + var p = m.GetParameters(); + + if (p.Length != parameterTypes.Length) return false; + + return !p.Where((t, index) => t.ParameterType != parameterTypes[index]).Any(); + }); + } + + var method = new DynamicMethod("Add", typeof(void), + new[] { typeof(ConfigurationElementCollection), typeof(ConfigurationElement) }, + typeof(CommonSectionBuilder).Module, true); + + var il = method.GetILGenerator(); + + //BaseRemove(GetElementKey(element)); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Callvirt, GetMethod("GetElementKey")); + il.Emit(OpCodes.Call, GetMethod("BaseRemove", typeof(object))); + + //BaseAdd(element); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Callvirt, GetMethod("BaseAdd", typeof(ConfigurationElement))); + + il.Emit(OpCodes.Ret); + + return (Action)method.CreateDelegate(typeof(Action)); + } +} diff --git a/Apollo.ConfigurationManager/ConfigExtensions.cs b/Apollo.ConfigurationManager/ConfigExtensions.cs index 99b5ce1..63a0847 100644 --- a/Apollo.ConfigurationManager/ConfigExtensions.cs +++ b/Apollo.ConfigurationManager/ConfigExtensions.cs @@ -1,25 +1,63 @@ -namespace Com.Ctrip.Framework.Apollo; +using System.Configuration; + +namespace Com.Ctrip.Framework.Apollo; internal static class ConfigExtensions { public static IEnumerable GetChildren(this IConfig config, string keyPrefix) { - keyPrefix += ":"; - var hash = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var propertyName in config.GetPropertyNames()) + if (string.IsNullOrWhiteSpace(keyPrefix)) { - if (!propertyName.StartsWith(keyPrefix, StringComparison.OrdinalIgnoreCase)) continue; + foreach (var propertyName in config.GetPropertyNames()) + { + var index = propertyName.IndexOf(':'); - var index = propertyName.IndexOf(':', keyPrefix.Length); + hash.Add(index > 0 ? propertyName.Substring(0, index) : propertyName); + } + } + else + { + keyPrefix += ":"; - hash.Add(index > 0 ? propertyName.Substring(0, index) : propertyName); + foreach (var propertyName in config.GetPropertyNames()) + { + if (!propertyName.StartsWith(keyPrefix, StringComparison.OrdinalIgnoreCase)) continue; + + var index = propertyName.IndexOf(':', keyPrefix.Length); + + hash.Add(index > 0 ? propertyName.Substring(0, index) : propertyName); + } } return hash.Select(key => new ConfigKey(key.Substring(key.LastIndexOf(':') + 1), key)); } + public static IEnumerable GetConnectionStrings(this IConfig config, + string keyPrefix, string? defaultProviderName) + { + string keyPrefixAndColon; + if (string.IsNullOrWhiteSpace(keyPrefix)) keyPrefixAndColon = keyPrefix = ""; + else keyPrefixAndColon = keyPrefix + ":"; + + foreach (var name in config.GetChildren(keyPrefix)) + { + var connectionName = name.Name; + + if (!config.TryGetProperty($"{keyPrefixAndColon}{connectionName}:ConnectionString", out var connectionString) && + !config.TryGetProperty($"{keyPrefixAndColon}{connectionName}", out connectionString) || + string.IsNullOrWhiteSpace(connectionString)) continue; + + config.TryGetProperty($"{keyPrefixAndColon}{connectionName}:ProviderName", out var providerName); + + yield return new ConnectionStringSettings(connectionName, connectionString, providerName ?? defaultProviderName); + } + } + + public static IConfig WithPrefix(this IConfig config, string? keyPrefix) => + string.IsNullOrWhiteSpace(keyPrefix) ? config : new KeyPrefixConfig(config, keyPrefix!); + public struct ConfigKey { public ConfigKey(string name, string fullName) @@ -33,4 +71,31 @@ public ConfigKey(string name, string fullName) public string FullName { get; } } -} \ No newline at end of file + + private class KeyPrefixConfig : IConfig + { + private readonly IConfig _config; + private readonly string _keyPrefix; + + public KeyPrefixConfig(IConfig config, string keyPrefix) + { + _config = config; + + _keyPrefix = keyPrefix + ":"; + } + + public bool TryGetProperty(string key, [NotNullWhen(true)] out string? value) => + _config.TryGetProperty(_keyPrefix + key, out value); + + public IEnumerable GetPropertyNames() => _config.GetPropertyNames() + .Where(propertyName => propertyName.Length > _keyPrefix.Length && + propertyName.StartsWith(_keyPrefix, StringComparison.OrdinalIgnoreCase)) + .Select(propertyName => propertyName.Substring(_keyPrefix.Length)); + + public event ConfigChangeEvent? ConfigChanged + { + add => _config.ConfigChanged += value; + remove => _config.ConfigChanged -= value; + } + } +} diff --git a/Apollo.ConfigurationManager/ConnectionStringsSectionBuilder.cs b/Apollo.ConfigurationManager/ConnectionStringsSectionBuilder.cs index 249c7ac..d9dcfa1 100644 --- a/Apollo.ConfigurationManager/ConnectionStringsSectionBuilder.cs +++ b/Apollo.ConfigurationManager/ConnectionStringsSectionBuilder.cs @@ -12,7 +12,7 @@ public override void Initialize(string name, NameValueCollection config) { base.Initialize(name, config); - _keyPrefix = config["keyPrefix"]; + _keyPrefix = config["keyPrefix"]?.TrimEnd(':'); _defaultProviderName = config["defaultProviderName"] ?? "System.Data.SqlClient"; } @@ -25,26 +25,14 @@ public override ConfigurationSection ProcessConfigurationSection(ConfigurationSe lock (this) { - var config = GetConfig(); - - if (string.IsNullOrEmpty(_keyPrefix)) _keyPrefix = configSection.SectionInformation.Name; - - foreach (var name in config.GetChildren(_keyPrefix!)) + foreach (var connectionString in GetConfig().GetConnectionStrings(_keyPrefix ?? configSection.SectionInformation.Name, _defaultProviderName)) { - var connectionName = name.Name; - - if (!config.TryGetProperty($"{_keyPrefix}:{connectionName}:ConnectionString", out var connectionString) && - !config.TryGetProperty($"{_keyPrefix}:{connectionName}", out connectionString) || - string.IsNullOrWhiteSpace(connectionString)) continue; - - connectionStrings.Remove(connectionName); - - config.TryGetProperty($"{_keyPrefix}:{connectionName}:ProviderName", out var providerName); + connectionStrings.Remove(connectionString.Name); - connectionStrings.Add(new ConnectionStringSettings(connectionName, connectionString, providerName ?? _defaultProviderName)); + connectionStrings.Add(connectionString); } } return base.ProcessConfigurationSection(configSection); } -} \ No newline at end of file +} diff --git a/Apollo.ConfigurationManager/Key2XmlConfigurationBuilder.cs b/Apollo.ConfigurationManager/Key2XmlConfigurationBuilder.cs deleted file mode 100644 index d966d2c..0000000 --- a/Apollo.ConfigurationManager/Key2XmlConfigurationBuilder.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Xml; - -namespace Com.Ctrip.Framework.Apollo; - -public class Key2XmlConfigurationBuilder : ApolloConfigurationBuilder -{ - private string? _keyPrefix; - - public override void Initialize(string name, NameValueCollection config) - { - base.Initialize(name, config); - - _keyPrefix = config["keyPrefix"]; - } - - public override XmlNode ProcessRawXml(XmlNode rawXml) - { - rawXml.Bind(GetConfig(), string.IsNullOrEmpty(_keyPrefix) ? rawXml.Name : _keyPrefix!); - - return base.ProcessRawXml(rawXml); - } -} \ No newline at end of file diff --git a/Apollo.ConfigurationManager/NodeReplaceSectionBuilder.cs b/Apollo.ConfigurationManager/NodeReplaceSectionBuilder.cs index 3feac92..3ae699f 100644 --- a/Apollo.ConfigurationManager/NodeReplaceSectionBuilder.cs +++ b/Apollo.ConfigurationManager/NodeReplaceSectionBuilder.cs @@ -16,10 +16,10 @@ public override void Initialize(string name, NameValueCollection config) public override XmlNode ProcessRawXml(XmlNode rawXml) { - if (string.IsNullOrEmpty(_key)) _key = rawXml.Name; + if (string.IsNullOrWhiteSpace(_key)) _key = rawXml.Name; if (!GetConfig().TryGetProperty(_key!, out var xml) || - string.IsNullOrEmpty(xml)) + string.IsNullOrWhiteSpace(xml)) return base.ProcessRawXml(rawXml); var doc = new XmlDocument(); @@ -35,4 +35,4 @@ public override XmlNode ProcessRawXml(XmlNode rawXml) return base.ProcessRawXml(doc.DocumentElement!); } -} \ No newline at end of file +} diff --git a/Apollo.ConfigurationManager/README.md b/Apollo.ConfigurationManager/README.md index 5bc2376..795da21 100644 --- a/Apollo.ConfigurationManager/README.md +++ b/Apollo.ConfigurationManager/README.md @@ -1,9 +1,9 @@ -# 一、准备工作 +# 一、准备工作 > 如果想将传统的config配置(如web.config)转成json配置,可以使用[config2json](https://github.com/andrewlock/dotnet-config2json)工具 ## 1.1 环境要求 - + * NETFramework 4.5+ * NETFramework 4.7.1+(支持[ConfigurationBuilder](https://docs.microsoft.com/zh-cn/dotnet/api/system.configuration.configurationbuilder)) @@ -194,36 +194,65 @@ apollo.net项目中有多个样例客户端的项目: # 四、NETFramework 4.7.1+ ConfigurationBuilder支持 -## 4.1 ApolloConfigurationBuilder说明 +## 4.1 ApolloConfigurationBuilder ``` xml - + - ``` * namespace为可选值,该值对应apollo中的namespace。支持多个值,以`,`或`;`分割,优先级从低到高 +* keyPrefix为可选值,当值不是IsNullOrWhiteSpace时生效 -## 4.2 ConnectionStringsSectionBuilder使用说明 +## 4.2 ConnectionStringsSectionBuilder ``` xml - + - ``` * namespace为可选值,该值对应apollo中的namespace。支持多个值,以`,`或`;`分割,优先级从低到高 * defaultProviderName为可选值,默认值为System.Data.SqlClient,,对应ConnectionString的ProviderName。 -* key必须以ConnectionStrings:开始 +* keyPrefix为可选值,没有配置时值为节点名(一般是connectionStrings),值是WhiteSpace时则不生效 * 通过ConnectionStrings:ConnectionName:ConnectionString或者ConnectionStrings:ConnectionName来设置连接字符串(同时指定时ConnectionStrings:ConnectionName:ConnectionString优先级高) * 通过ConnectionStrings:ConnectionName:ProviderName来指定使用其他数据库,比如MySql.Data.MySqlClient来指定是MySql +## 4.3 CommonSectionBuilder + +通过反射结点类型,动态递归添加到节点类型中,此方法灵活,适应绝大部分节点。通过读取属性的[ConfigurationProperty]值和配置中的值关联 + +``` xml + + + + + + + +``` +* namespace为可选值,该值对应apollo中的namespace。支持多个值,以`,`或`;`分割,优先级从低到高 +* keyPrefix为可选值,没有配置时值为节点名,值是WhiteSpace时则不生效 + +## 4.4 NodeReplaceSectionBuilder +直接使用apollo中配置的xml替换掉原来的节点xml +``` xml + + + + + + + +``` +* namespace为可选值,该值对应apollo中的namespace。支持多个值,以`,`或`;`分割,优先级从低到高 +* key为可选值,当为IsNullOrWhiteSpace时值为节点名 + # 五、FAQ ## 5.1 Apollo内部HttpClient如何配置代理 diff --git a/Apollo.ConfigurationManager/XmlExtensions.cs b/Apollo.ConfigurationManager/XmlExtensions.cs deleted file mode 100644 index 5b060d5..0000000 --- a/Apollo.ConfigurationManager/XmlExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Xml; - -namespace Com.Ctrip.Framework.Apollo; - -internal static class XmlExtensions -{ - public static void Bind(this XmlNode? xmlNode, IConfig config, string keyPrefix) - { - if (xmlNode == null) return; - - foreach (var key in config.GetChildren(keyPrefix)) - { - if (config.GetChildren(key.FullName).Any()) - CreateXmlNode(xmlNode, key.Name).Bind(config, key.FullName); - else - AddOrUpdateAttribute(xmlNode, key.Name, config.GetProperty(key.FullName, "")); - } - } - - private static void AddOrUpdateAttribute(XmlNode xmlNode, string name, string value) - { - if (xmlNode.Attributes == null) return; - - foreach (XmlAttribute attr in xmlNode.Attributes) - { - if (attr.Name != name) continue; - - attr.Value = value; - - return; - } - - if (xmlNode.OwnerDocument == null) return; - - var createAttribute = xmlNode.OwnerDocument.CreateAttribute(name, xmlNode.NamespaceURI); - - createAttribute.Value = value; - - xmlNode.Attributes.Append(createAttribute); - } - - private static XmlNode? CreateXmlNode(XmlNode? xmlNode, string nodeName) - { - if (xmlNode?.OwnerDocument == null || string.IsNullOrEmpty(nodeName)) return null; - - if (xmlNode[nodeName] == null) - xmlNode.AppendChild(xmlNode.OwnerDocument.CreateElement(nodeName)); - - return xmlNode[nodeName]; - } -} \ No newline at end of file diff --git a/Apollo.ExtensionsHosting/Apollo.ExtensionsHosting.csproj b/Apollo.ExtensionsHosting/Apollo.ExtensionsHosting.csproj index ee464c8..185912c 100644 --- a/Apollo.ExtensionsHosting/Apollo.ExtensionsHosting.csproj +++ b/Apollo.ExtensionsHosting/Apollo.ExtensionsHosting.csproj @@ -14,7 +14,7 @@ $(PackageReleaseNotes) $(RepositoryUrl)/$(MSBuildProjectName) Microsoft.Extensions.Hosting netstandard2.0 - $(ApolloVersion) + $(ApolloVersion).0 diff --git a/Apollo/Apollo.csproj b/Apollo/Apollo.csproj index c9ec631..8a1e6ee 100644 --- a/Apollo/Apollo.csproj +++ b/Apollo/Apollo.csproj @@ -14,7 +14,7 @@ $(PackageReleaseNotes) $(RepositoryUrl)/$(MSBuildProjectName) Com.Ctrip.Framework.Apollo net40;net45;netstandard2.0;netstandard2.1 - $(ApolloVersion) + $(ApolloVersion).0 diff --git a/Directory.Build.props b/Directory.Build.props index 4f1a5c9..b66811d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@  - 2.5.0 + 2.5 ..\Apollo.snk 2.0.0 True