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