using System.Xml; namespace NetAdmin.Infrastructure.Utils; /// /// 程序集注释文档读取器 /// public sealed class XmlCommentReader : ISingleton { private const string _XPATH = "//doc/members/member[@name=\"{0}\"]"; private static readonly Regex _regex = new(@"`\d+"); private readonly List _xmlDocuments = new(); /// /// Initializes a new instance of the class. /// public XmlCommentReader(IOptions specificationDocumentSettings) { var xmlComments = specificationDocumentSettings.Value.XmlComments // ?? App.GetConfig( nameof(SpecificationDocumentSettingsOptions).TrimEndOptions()) .XmlComments; foreach (var commentFile in xmlComments.Where(x => x.Contains(nameof(NetAdmin)))) { var xmlDoc = new XmlDocument(); var xmlFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, commentFile); if (!File.Exists(xmlFilePath)) { LogHelper.Get().Warn($"{Ln.XML注释文件不存在}: {xmlFilePath}"); continue; } xmlDoc.Load(xmlFilePath); _xmlDocuments.Add(xmlDoc); } } /// /// 获取指定类型的注释 /// /// InvalidCastException public string GetComments(MemberInfo memberInfo) { var node = memberInfo switch { MethodInfo method => GetNodeByMethod(method) , Type type => GetNodeByType(type) , _ => throw new InvalidCastException() }; if (node?.FirstChild?.Name != "inheritdoc") { return node?.FirstChild?.InnerText.Trim(); } var cref = node.FirstChild.Attributes?["cref"]?.Value; if (cref != null) { return GetComments(App.EffectiveTypes.Single(x => x.FullName == cref[2..])); } var methodFromBaseType = memberInfo.DeclaringType?.BaseType?.GetMethod(memberInfo.Name); if (methodFromBaseType != null) { return GetComments(methodFromBaseType); } var methodFromInterface = memberInfo.DeclaringType?.GetInterfaces() .Select(x => x.GetMethod(memberInfo.Name)) .FirstOrDefault(x => x != null); return methodFromInterface == null ? null : GetComments(methodFromInterface); } private XmlNode GetNodeByMethod(MethodInfo method) { var nodeName = $"M:{method.DeclaringType}.{method.Name}"; var parameters = method.GetParameters(); if (parameters.Length != 0) { nodeName += $"({string.Join(',', parameters.Select(Replace))})"; } return _xmlDocuments.Select(xmlDoc => xmlDoc.SelectSingleNode( #pragma warning disable CA1863 string.Format(NumberFormatInfo.InvariantInfo, _XPATH, nodeName))) #pragma warning restore CA1863 .FirstOrDefault(ret => ret != null); static string Replace(ParameterInfo parameterInfo) { return _regex.Replace(parameterInfo.ParameterType.ToString(), string.Empty) .Replace("[", "{") .Replace("]", "}"); } } private XmlNode GetNodeByType(Type type) { return _xmlDocuments.Select(xmlDoc => xmlDoc.SelectSingleNode( #pragma warning disable CA1863 string.Format(NumberFormatInfo.InvariantInfo, _XPATH, $"T:{type.FullName}"))) #pragma warning restore CA1863 .FirstOrDefault(ret => ret != null); } }