Подтвердить что ты не робот

Программно получать сводные комментарии во время выполнения

Я ищу способ программно получить сводную часть Xml-комментариев метода в ASP.net.

Я посмотрел на предыдущие связанные посты, и они не предоставляют способ сделать это в веб-среде.

Я не могу использовать какие-либо сторонние приложения, и из-за веб-среды плагин для Visual Studio также мало используется.

Самым близким, что я нашел к рабочему решению, был проект JimBlackler, но он работает только на DLL.

Естественно, что-то вроде "предоставить файл .CS, получить документацию XML" будет оптимальным.


Текущая ситуация

У меня есть веб-сервис, и я пытаюсь динамически сгенерировать для него документацию.

Читать методы и свойства легко, но получение сводки по каждому методу меня немного разочаровывает.

/// <summary>
/// This Is what I'm trying to read
/// </summary>
public class SomeClass()
{
    /// <summary>
    /// This Is what I'm trying to read
    /// </summary>
    public void SomeMethod()
    {
    }
}

4b9b3361

Ответ 1

Резюме XML не сохраняется в сборке .NET - оно может быть записано в файл XML как часть вашей сборки (при условии, что вы используете Visual Studio).

Следовательно, нет возможности "вытащить" резюме XML каждого метода через отражение на скомпилированной сборке .NET(либо .EXE, либо. DLL), потому что данных просто нет для вас, чтобы вытащить. Если вам нужны данные, вам нужно будет указать среду сборки для вывода XML файлов как часть процесса сборки и проанализировать эти файлы XML во время выполнения, чтобы получить итоговую информацию.

Ответ 2

Обходной путь - использование отражения в программе .DLL/EXE вместе с файлом Program.XML

Если вы посмотрите на XML файл sibling, созданный Visual Studio, вы увидите, что существует довольно плоская иерархия /members/member. Все, что вам нужно сделать, это получить доступ к каждому методу из вашей DLL через объект MethodInfo. Когда у вас есть этот объект, вы переходите к XML и используете XPATH, чтобы получить член, содержащий XML-документацию для этого метода.

Пользователям предшествует письмо. XML-документу для методов предшествует "M:" для класса "T:" и т.д.

Загрузите свой родной язык XML

string docuPath = dllPath.Substring(0, dllPath.LastIndexOf(".")) + ".XML";

if (File.Exists(docuPath))
{
  _docuDoc = new XmlDocument();
  _docuDoc.Load(docuPath);
}

Используйте этот xpath, чтобы получить член, представляющий метод XML document

string path = "M:" + mi.DeclaringType.FullName + "." + mi.Name;

XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
    "//member[starts-with(@name, '" + path + "')]");

Теперь сканируйте дочерние коды для всех строк "///" Иногда ///Summary содержит дополнительные пробелы, если это беспокоит использование этого для удаления

var cleanStr = Regex.Replace(row.InnerXml, @"\s+", " ");

Ответ 3

Вы можете "документировать" свой метод, используя атрибут System.ComponentModel.DataAnnotations.DisplayAttribute, например

[Display(Name = "Foo", Description = "Blah")]
void Foo()
{
}

затем используйте отражение, чтобы вывести описание во время выполнения.

Ответ 4

Удаленная запись, сделанная @OleksandrIeremenko, в этой теме ссылается на эту статью https://jimblackler.net/blog/?p=49, которая послужила основой для моего решения.

Ниже приведена модификация кода Джима Блэклера, делающая методы расширения для объектов MemberInfo и Type, и добавление кода, который возвращает текст резюме или пустую строку, если она недоступна.

использование

var typeSummary = typeof([Type Name]).GetSummary();
var methodSummary = typeof([Type Name]).GetMethod("[Method Name]").GetSummary();

Расширение класса

/// <summary>
/// Utility class to provide documentation for various types where available with the assembly
/// </summary>
public static class DocumenationExtensions
{
    /// <summary>
    /// Provides the documentation comments for a specific method
    /// </summary>
    /// <param name="methodInfo">The MethodInfo (reflection data ) of the member to find documentation for</param>
    /// <returns>The XML fragment describing the method</returns>
    public static XmlElement GetDocumentation(this MethodInfo methodInfo)
    {
        // Calculate the parameter string as this is in the member name in the XML
        var parametersString = "";
        foreach (var parameterInfo in methodInfo.GetParameters())
        {
            if (parametersString.Length > 0)
            {
                parametersString += ",";
            }

            parametersString += parameterInfo.ParameterType.FullName;
        }

        //AL: 15.04.2008 ==> BUG-FIX remove "()" if parametersString is empty
        if (parametersString.Length > 0)
            return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name + "(" + parametersString + ")");
        else
            return XmlFromName(methodInfo.DeclaringType, 'M', methodInfo.Name);
    }

    /// <summary>
    /// Provides the documentation comments for a specific member
    /// </summary>
    /// <param name="memberInfo">The MemberInfo (reflection data) or the member to find documentation for</param>
    /// <returns>The XML fragment describing the member</returns>
    public static XmlElement GetDocumentation(this MemberInfo memberInfo)
    {
        // First character [0] of member type is prefix character in the name in the XML
        return XmlFromName(memberInfo.DeclaringType, memberInfo.MemberType.ToString()[0], memberInfo.Name);
    }
    /// <summary>
    /// Returns the Xml documenation summary comment for this member
    /// </summary>
    /// <param name="memberInfo"></param>
    /// <returns></returns>
    public static string GetSummary(this MemberInfo memberInfo)
    {
        var element = memberInfo.GetDocumentation();
        var summaryElm = element?.SelectSingleNode("summary");
        if (summaryElm == null) return "";
        return summaryElm.InnerText.Trim();
    }

    /// <summary>
    /// Provides the documentation comments for a specific type
    /// </summary>
    /// <param name="type">Type to find the documentation for</param>
    /// <returns>The XML fragment that describes the type</returns>
    public static XmlElement GetDocumentation(this Type type)
    {
        // Prefix in type names is T
        return XmlFromName(type, 'T', "");
    }

    /// <summary>
    /// Gets the summary portion of a type documenation or returns an empty string if not available
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static string GetSummary(this Type type)
    {
        var element = type.GetDocumentation();
        var summaryElm = element?.SelectSingleNode("summary");
        if (summaryElm == null) return "";
        return summaryElm.InnerText.Trim();
    }

    /// <summary>
    /// Obtains the XML Element that describes a reflection element by searching the 
    /// members for a member that has a name that describes the element.
    /// </summary>
    /// <param name="type">The type or parent type, used to fetch the assembly</param>
    /// <param name="prefix">The prefix as seen in the name attribute in the documentation XML</param>
    /// <param name="name">Where relevant, the full name qualifier for the element</param>
    /// <returns>The member that has a name that describes the specified reflection element</returns>
    private static XmlElement XmlFromName(this Type type, char prefix, string name)
    {
        string fullName;

        if (string.IsNullOrEmpty(name))
            fullName = prefix + ":" + type.FullName;
        else
            fullName = prefix + ":" + type.FullName + "." + name;

        var xmlDocument = XmlFromAssembly(type.Assembly);

        var matchedElement = xmlDocument["doc"]["members"].SelectSingleNode("member[@name='" + fullName + "']") as XmlElement;

        return matchedElement;
    }

    /// <summary>
    /// A cache used to remember Xml documentation for assemblies
    /// </summary>
    private static readonly Dictionary<Assembly, XmlDocument> Cache = new Dictionary<Assembly, XmlDocument>();

    /// <summary>
    /// A cache used to store failure exceptions for assembly lookups
    /// </summary>
    private static readonly Dictionary<Assembly, Exception> FailCache = new Dictionary<Assembly, Exception>();

    /// <summary>
    /// Obtains the documentation file for the specified assembly
    /// </summary>
    /// <param name="assembly">The assembly to find the XML document for</param>
    /// <returns>The XML document</returns>
    /// <remarks>This version uses a cache to preserve the assemblies, so that 
    /// the XML file is not loaded and parsed on every single lookup</remarks>
    public static XmlDocument XmlFromAssembly(this Assembly assembly)
    {
        if (FailCache.ContainsKey(assembly))
        {
            throw FailCache[assembly];
        }

        try
        {

            if (!Cache.ContainsKey(assembly))
            {
                // load the docuemnt into the cache
                Cache[assembly] = XmlFromAssemblyNonCached(assembly);
            }

            return Cache[assembly];
        }
        catch (Exception exception)
        {
            FailCache[assembly] = exception;
            throw exception;
        }
    }

    /// <summary>
    /// Loads and parses the documentation file for the specified assembly
    /// </summary>
    /// <param name="assembly">The assembly to find the XML document for</param>
    /// <returns>The XML document</returns>
    private static XmlDocument XmlFromAssemblyNonCached(Assembly assembly)
    {
        var assemblyFilename = assembly.CodeBase;

        const string prefix = "file:///";

        if (assemblyFilename.StartsWith(prefix))
        {
            StreamReader streamReader;

            try
            {
                streamReader = new StreamReader(Path.ChangeExtension(assemblyFilename.Substring(prefix.Length), ".xml"));
            }
            catch (FileNotFoundException exception)
            {
                throw new Exception("XML documentation not present (make sure it is turned on in project properties when building)", exception);
            }

            var xmlDocument = new XmlDocument();
            xmlDocument.Load(streamReader);
            return xmlDocument;
        }
        else
        {
            throw new Exception("Could not ascertain assembly filename", null);
        }
    }
}

Ответ 5

Вы можете посмотреть https://github.com/NSwag/NSwag - источник для nuget NSwag.CodeGeneration - он также получает сводку, использование

var generator = new WebApiAssemblyToSwaggerGenerator(settings);<br/>
var swaggerService = generator.GenerateForController("namespace.someController");<br/>
// string with comments <br/>
var swaggerJson = swaggerService.ToJson(); 

(попробуйте декомпилятор ILSPY против вашей DLL, вы проверяете код и комментарии)

Ответ 6

Если у вас есть доступ к исходному коду, на который вы пытаетесь получить комментарии, вы можете использовать платформу компилятора Roslyn, чтобы сделать это, Это в основном дает вам доступ ко всем метаданным промежуточного компилятора, и вы можете делать с ним все, что хотите.

Это немного сложнее, чем предлагают другие люди, но в зависимости от ваших потребностей может быть вариантом.

Похоже, этот пост имеет пример кода для чего-то подобного.