.netnugetxml-documentationdocfx

Retrieve DocFx metadata from NuGet packages


I have a product with multiple repositories, each distributed as a NuGet package. I would like to establish a separate repository for DocFx, which generates the documentation from the NuGet packages. That way, it can consolidate the documentation for multiple { repositories, projects, packages } into one location, while also allowing conceptual documentation to be updated independent of the package release cycle.

TL;DR: I am able to get DocFx to correctly generate the reference documentation based on the assemblies in the NuGet package, but it doesn't include any of the XML documentation (i.e., from /// metadata). How do I solve this? Details below.

Source Package Configuration

To pursue this, I first ensured that the csproj files for the source projects were configured to generate the XML documentation as part of the build output:

<Project>
  <PropertyGroup>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>  
</Project>

And, as a result, the nupkg file correctly includes the XML documentation at e.g.:

/lib/net6.0/{AssemblyName}.xml

This documentation is then picked up by e.g. Visual Studio IntelliSense when consuming these packages.

DocFx Configuration

I then added metadata for these assembles via the docfx.json configuration file:

{
  "metadata": [
    {
      "src": [
        {
          "files": "bin/Release/net6.0/{AssemblyName}.dll"
        }
      ],
      "dest": "api/{AssemblyName}"
    }
  ]
  …
}

With this in place, DocFx correctly identifies the namespaces, types, and members, and generates reference documentation for each. So far, so good!

The Problem

The problem is that DocFx doesn't include any of the XML Doc (///) information for these assemblies, despite them being included with the package. How do I mitigate this?


Solution

  • If you specify an assembly in the metadata configuration, DocFx will look for the XML documentation in the same directory (source). The problem, however, is that when you include a NuGet reference, that XML documentation isn't included in your project's bin folder, even if it's included in the NuGet package. As a result, while DocFx is able to identify the structure, it is not able to identify the documentation.

    TL;DR: To resolve this, you can configure msbuild to copy the XML documentation to the bin folder, ensuring DocFx has access to it (example). Details below.

    Background

    When you reference a NuGet package, by default, the package is downloaded, unzipped, and cached in the following location:

    %UserProfile%\.nuget\packages\{Package}\{Version}\
    

    Note: This is obviously a Windows directory; the location will vary by operating system.

    This will include e.g.,

    \lib\net6.0\{AssemblyName}.dll
    \lib\net6.0\{AssemblyName}.xml
    

    When you build your .NET project, the {AssemblyName}.dll is copied to your project's bin directory, but not the {AssemblyName}.xml. Since you've instructed DocFx to look in your project's bin folder, it is therefore unable to collect the documentation.

    Naïve Workaround

    The obvious workaround would be to simply source the metadata from the NuGet cache directly. This can easily be accomplished by adjusting the docfx.json configuration as follows:

    {
      "metadata": [
        {
          "src": [
            {
              "files": "bin/Release/net6.0/{AssemblyName}.dll",
              "src": "C:\\Users\\{User}\\.nuget\\packages\\{Package}\\{Version}\\lib\\net6.0\\"
            }
          ],
          "dest": "api/{AssemblyName}"
        }
      ]
      …
    }
    

    This works, of course. But it's also a really brittle solution that isn't feasible for production code. Most notably, DocFx doesn't recognize the %UserProfile% alias, and so the {User} must be hardcoded into the configuration. Not only will this be different for each developer, but it will also be different on the build server. And that's not even factoring in non-Windows environments.

    A better solution

    Given this, the better solution is to instruct msbuild to copy the XML documentation to the bin folder along with the assembly. Fortunately, that's pretty easy, as discussed in How to include XML Documentation from Nuget Package in csproj build output. In your csproj file, simply add the following:

    <Project>
      <Target Name="_ResolveCopyLocalNuGetPkgXmls" AfterTargets="ResolveReferences">
        <ItemGroup>
          <ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')"
          Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)'!='' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
        </ItemGroup>
      </Target>
    </Project>
    

    The main advantage of this approach is that it relies on msbuild variable to abstract the location of the NuGet cache, the package name, the version number, and the destination folder. As a result, this should work on any machine or operating system.

    With this in place, DocFx will successfully find the XML documentation alongside the assembly, and include it in the generated yml metadata.