XmlFileCfgSource.cs 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using System.Xml;
  2. using Apq.Cfg.Sources;
  3. using Apq.Cfg.Sources.File;
  4. using Microsoft.Extensions.Configuration;
  5. using Microsoft.Extensions.Configuration.Xml;
  6. using Microsoft.Extensions.FileProviders;
  7. namespace Apq.Cfg.Xml;
  8. internal sealed class XmlFileCfgSource : FileCfgSourceBase, IWritableCfgSource
  9. {
  10. public XmlFileCfgSource(string path, int level, bool writeable, bool optional, bool reloadOnChange,
  11. bool isPrimaryWriter)
  12. : base(path, level, writeable, optional, reloadOnChange, isPrimaryWriter)
  13. {
  14. }
  15. public override IConfigurationSource BuildSource()
  16. {
  17. var (fp, file) = CreatePhysicalFileProvider(_path);
  18. var src = new XmlConfigurationSource
  19. {
  20. FileProvider = fp,
  21. Path = file,
  22. Optional = _optional,
  23. ReloadOnChange = _reloadOnChange
  24. };
  25. src.ResolveFileProvider();
  26. return src;
  27. }
  28. public async Task ApplyChangesAsync(IReadOnlyDictionary<string, string?> changes, CancellationToken cancellationToken)
  29. {
  30. if (!IsWriteable)
  31. throw new InvalidOperationException($"配置源 (层级 {Level}) 不可写");
  32. EnsureDirectoryFor(_path);
  33. var doc = new XmlDocument();
  34. if (File.Exists(_path))
  35. {
  36. var readEncoding = DetectEncodingEnhanced(_path);
  37. using var sr = new StreamReader(_path, readEncoding, true);
  38. doc.Load(sr);
  39. }
  40. else
  41. {
  42. var decl = doc.CreateXmlDeclaration("1.0", "utf-8", null);
  43. doc.AppendChild(decl);
  44. var root = doc.CreateElement("configuration");
  45. doc.AppendChild(root);
  46. }
  47. foreach (var (key, value) in changes)
  48. SetXmlByColonKey(doc, key, value);
  49. using var ms = new MemoryStream();
  50. using (var writer = XmlWriter.Create(ms, new XmlWriterSettings { Indent = true, Encoding = GetWriteEncoding() }))
  51. {
  52. doc.Save(writer);
  53. }
  54. var bytes = ms.ToArray();
  55. await File.WriteAllBytesAsync(_path, bytes, cancellationToken).ConfigureAwait(false);
  56. }
  57. private static void SetXmlByColonKey(XmlDocument doc, string key, string? value)
  58. {
  59. var parts = key.Split(':', StringSplitOptions.RemoveEmptyEntries);
  60. var node = doc.DocumentElement!;
  61. for (var i = 0; i < parts.Length; i++)
  62. {
  63. var name = parts[i];
  64. var child = node.SelectSingleNode(name) as XmlElement;
  65. if (child == null)
  66. {
  67. child = doc.CreateElement(name);
  68. node.AppendChild(child);
  69. }
  70. node = child;
  71. }
  72. node.InnerText = value ?? string.Empty;
  73. }
  74. }