DTO comments from external assembly in Swagger documentation in ASP.NET Core

Involving contract classes documentation from separate assemblies to Swagger

Contracts, models or DTOs, call them how ever you want, are your connection with the outside world from your REST API. Consumers of your API depend on them and they know what to expect or what your API endpoints expect in communication. That is why it is important that you document the DTOs properly so that the third party who works on the consumer. Luckily for us there is Swagger which can easily do this for you. All you have to do is to write comments for your DTO classes and their properties.

I will not go into details in how to setup Swagger from the scratch for ASP.NET Core project. If you are new to Swagger, you can check Setting up Swagger to support versioned API endpoints in ASP.NET Core article where you can find how to setup Swagger for your ASP.NET Core WebAPI project.

The problem we are going to focus here is including the documentation from other projects in our ASP.NET Core WebAPI solution. Quite often our DTOs need to be accessed from other onion layers of our solution. For example, you might have to share your DTOs with the application services layer which can take care of the mapping to and from entities or even do the validation for you, so your facade stays clean and does not interfere with anything except the routing and serialization.

 Dto

In order for Swagger to build the documentation from the comments from your code you need to include the output XML files of the assemblies. By including only the executing assembly, unless your DTOs are not declared in the WebAPI project, Swagger will not generate the documentation for them.

  services.AddSwaggerGen(  
        options =>  
        {  
                       // Tells swagger to pick up the output XML document file  
            options.IncludeXmlComments(Path.Combine(   
                Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), $"{this.GetType().Assembly.GetName().Name}.xml"  
                ));  
  
        });  
    

To tell swagger to to involve the contracts project xml comments you would have to add the path to the xml output file from the contracts project. Of course, you need to set the path for the documentation in the contracts project.

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(MSBuildThisFileName).xml</DocumentationFile>
    <PackageId>Sample.Versioning.Swagger</PackageId>
    <Product>Sample service</Product>
    <Description>Contracts project</Description>
  </PropertyGroup>
    

This will cause build of the project to output xml document along with the assembly of compiled project inside bin folder.

Note

It is important to have the same file name of the XML document as the name of the output assembly. This is achieved by using $(MSBuildThisFileName) in the project file DocumentationFile xml node value.

We can manually add the project xml files when initializing Swagger service in our startup, but as the project evolves, number of referenced libraries from the facade may grow. One of the scenarios is separating contracts for each API version into the separate projects. Doing this will push us do always list the output file in out Swagger initialization. Although this is not an often task, it is potential place for introducing a bug because, do not forget, your API consumers rely on the documentation you provide. The more accurate your documentation is, the less tasks you will have in your backlog.

Since all the contracts you will expose will have to be referenced to the API project, you can access them as the list of referenced assemblies with reflection by calling Assembly.GetReferencedAssemblies method on the currently executing assembly.

Assembly.GetExecutingAssembly().GetReferencedAssemblies();
    

From this collection we can easily build the xml document file paths and include all of the in the swagger initialization. 

Note

Apart from referencing assembling we also need to include the current executing assembly since it contains the documentation for our controllers. We also need to filter only assemblies which do have xml document generated. If you pass the path do swagger which does not exist, you will get the exception during the initialization.

                    //Collect all referenced projects output XML document file paths
                    var currentAssembly = Assembly.GetExecutingAssembly();
                    var xmlDocs = currentAssembly.GetReferencedAssemblies()
                    .Union(new AssemblyName[] { currentAssembly.GetName()})
                    .Select(a => Path.Combine(Path.GetDirectoryName(currentAssembly.Location), $"{a.Name}.xml"))
                    .Where(f=>File.Exists(f)).ToArray();
    

Now that we have paths of all the xml files generated on the build, we just need to include each of them to Swagger xml document collection

                    Array.ForEach(xmlDocs, (d) =>
                    {
                        options.IncludeXmlComments(d);
                    });
    

Regardless of how many projects you have with DTOs or any class that will be exposed in the facade Swagger documentation, they will be picked on the startup and included into the Swagger auto-generated documentation.

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article