Web Api Help Page XML comments from more than 1 files

こ雲淡風輕ζ 提交于 2019-11-28 17:34:39

You can modify the installed XmlDocumentationProvider at Areas\HelpPage to do something like following:

Merge multiple Xml document files into a single one:

Example code(is missing some error checks and validation):

using System.Xml.Linq;
using System.Xml.XPath;

 XDocument finalDoc = null;
 foreach (string file in Directory.GetFiles(@"PluginsFolder", "*.xml"))
 {
    if(finalDoc == null)
    {
        finalDoc = XDocument.Load(File.OpenRead(file));
    }
    else
    {
        XDocument xdocAdditional = XDocument.Load(File.OpenRead(file));

        finalDoc.Root.XPathSelectElement("/doc/members")
                     .Add(xdocAdditional.Root.XPathSelectElement("/doc/members").Elements());
    }
}

// Supply the navigator that rest of the XmlDocumentationProvider code looks for
_documentNavigator = finalDoc.CreateNavigator();

Kirans solution works very well. I ended up using his approach but by creating a copy of XmlDocumentationProvider, called MultiXmlDocumentationProvider, with an altered constructor:

public MultiXmlDocumentationProvider(string xmlDocFilesPath)
{
       XDocument finalDoc = null;
        foreach (string file in Directory.GetFiles(xmlDocFilesPath, "*.xml"))
        {
            using (var fileStream = File.OpenRead(file))
            {
                if (finalDoc == null)
                {
                    finalDoc = XDocument.Load(fileStream);
                }
                else
                {
                    XDocument xdocAdditional = XDocument.Load(fileStream);

                    finalDoc.Root.XPathSelectElement("/doc/members")
                        .Add(xdocAdditional.Root.XPathSelectElement("/doc/members").Elements());
                }
            }
        }

        // Supply the navigator that rest of the XmlDocumentationProvider code looks for
        _documentNavigator = finalDoc.CreateNavigator();
}

I register the new provider from HelpPageConfig.cs:

config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/")));

Creating a new class and leaving the original one unchanged may be more convenient when upgrading etc...

Rather than create a separate class along the lines of XmlMultiDocumentationProvider, I just added a constructor to the existing XmlDocumentationProvider. Instead of taking a folder name, this takes a list of strings so you can still specify exactly which files you want to include (if there are other xml files in the directory that the Documentation XML are in, it might get hairy). Here's my new constructor:

public XmlDocumentationProvider(IEnumerable<string> documentPaths)
{
    if (documentPaths.IsNullOrEmpty())
    {
        throw new ArgumentNullException(nameof(documentPaths));
    }
    XDocument fullDocument = null;
    foreach (var documentPath in documentPaths)
    {
        if (documentPath == null)
        {
            throw new ArgumentNullException(nameof(documentPath));
        }

        if (fullDocument == null)
        {
            using (var stream = File.OpenRead(documentPath))
            {
                fullDocument = XDocument.Load(stream);
            }
        }
        else
        {
            using (var stream = File.OpenRead(documentPath))
            {
                var additionalDocument = XDocument.Load(stream);
                fullDocument?.Root?.XPathSelectElement("/doc/members").Add(additionalDocument?.Root?.XPathSelectElement("/doc/members").Elements());
            }
        }
    }

    _documentNavigator = fullDocument?.CreateNavigator();
}

The HelpPageConfig.cs looks like this. (Yes, it can be fewer lines, but I don't have a line limit so I like splitting it up.)

var xmlPaths = new[]
{
    HttpContext.Current.Server.MapPath("~/bin/Path.To.FirstNamespace.XML"),
    HttpContext.Current.Server.MapPath("~/bin/Path.To.OtherNamespace.XML")
};
var documentationProvider = new XmlDocumentationProvider(xmlPaths);
config.SetDocumentationProvider(documentationProvider);

I agree with gurra777 that creating a new class is a safer upgrade path. I started with that solution but it involves a fair amount of copy/pasta, which could easily get out of date after a few package updates.

Instead, I am keeping a collection of XmlDocumentationProvider children. For each of the implementation methods, I'm calling into the children to grab the first non-empty result.

public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
{
    private IList<XmlDocumentationProvider> _documentationProviders;

    public MultiXmlDocumentationProvider(string xmlDocFilesPath)
    {
        _documentationProviders = new List<XmlDocumentationProvider>();

        foreach (string file in Directory.GetFiles(xmlDocFilesPath, "*.xml"))
        {
            _documentationProviders.Add(new XmlDocumentationProvider(file));
        }
    }

    public string GetDocumentation(System.Reflection.MemberInfo member)
    {
        return _documentationProviders
            .Select(x => x.GetDocumentation(member))
            .FirstOrDefault(x => !string.IsNullOrWhiteSpace(x));
    }

    //and so on...

The HelpPageConfig registration is the same as in gurra777's answer,

config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/")));
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!