Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

Automatic code documentation based on your C# comments

, 23 Apr 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
I’ve written a few APIs over the years and the worst part is writing the documentation: it takes extra time it must be updated every time you make changes to your code it is a duplication of work because I already document my code inline anyway So, here’s a handy utility I wrote which will […]

I’ve written a few APIs over the years and the worst part is writing the documentation:

  • it takes extra time
  • it must be updated every time you make changes to your code
  • it is a duplication of work because I already document my code inline anyway

So, here’s a handy utility I wrote which will use reflection to whip through your code and draw out the comments.

Generating XML Documentation

Before proceeding, you must setup your project to generate an XML file of your code comments. This is done via the Properties –> Build menu in your project (presumably it’s a web project). See this screenshot below:

Screenshot

This generates an xml file in the bin directory every time you build. The file contains all your code comments, ready for parsing by my helper utility. The format is something like this:

Screenshot

So, with this in place, here is the utility class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using Common;


namespace Web.Code
{
    /// <summary>
    /// Creates documentation for our various API methods
    /// </summary>
    public class ApiDocumentationGenerator
    {

        #region Sub classes

        public class Parameter
        {
            public string Name { get; set; }
            public string Type { get; set; }
            public string Description { get; set; }
        }

        public class Method
        {
            public string Name { get; set; }
            public List<Parameter> Parameters = new List<Parameter>();
            public string Summary { get; set; }
        }

        #endregion

        #region Properties

        public List<Method> Methods = new List<Method>();
        private string PathToXmlDocumentation = "";

        private XDocument _XmlDocumentation = null;
        private XDocument XmlDocumentation
        {
            get
            {
                if (_XmlDocumentation == null)
                {
                    _XmlDocumentation = XDocument.Load(this.PathToXmlDocumentation);
                }
                return _XmlDocumentation;
            }
        }

        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="pathToXmlDocumentationFile"></param>
        public ApiDocumentationGenerator(string pathToXmlDocumentationFile)
        {
            this.PathToXmlDocumentation = pathToXmlDocumentationFile;
        }

        /// <summary>
        /// Generates our classes
        /// </summary>
        public void Generate()
        {
            // BaseController is a class I wrote which all my MVC *Controller methods inherit from.  If you don't have a base class, you can just use
            // whatever parent class you know your own API methods sit within.  And if there is no parent class, then just get every time in the assembly
            var ass = System.Reflection.Assembly.GetAssembly(typeof(BaseController));

            // Get each class
            foreach (var controller in ass.GetTypes())
            {
                if (controller.IsSubclassOf(typeof(BaseController))) ExtractMethods(controller);
            }
        }

        /// <summary>
        /// Finds the methods in this controller
        /// </summary>
        /// <param name="controller"></param>
        private void ExtractMethods(Type controller)
        {
            foreach (var method in controller.GetMethods())
            {
                // My API methods are decorated with a custom attribute, ApiMethodAttribute, so only show those ones
                var attrs = System.Attribute.GetCustomAttributes(method);

                // Check our attributes show we have an API method
                var isAPIMethod = false;
                foreach (System.Attribute attr in attrs)
                {
                    if (attr is ApiMethodAttribute)
                    {
                        isAPIMethod = true;
                        break;
                    }
                }

                // Break if not an API method
                if (!isAPIMethod) continue;

                // Parse out properties
                var meth = new Method();
                meth.Name = controller.Name.Replace("Controller", "") + "/" + method.Name;
                this.Methods.Add(meth);

                // Quick hack to detect the XML segment we want - I know that all my API methods are in *Controller methods, so I can just restrict to this
                var memberName = "Controller." + method.Name;

                // Get the methods from our documentation
                var docInfo = (
                              from m in this.XmlDocumentation.Descendants("members").Descendants("member")
                              where m.Attribute("name").Value.Contains(memberName)
                              select new {
                                  Summary = m.Descendants("summary").First().Value,
                                  Params = m.Descendants("param")
                              }
                ).FirstOrDefault();

                // Now copy the XML back into my method/parameter classes
                if (docInfo != null)
                {
                    meth.Summary = docInfo.Summary;

                    // Add parameters
                    foreach (var param in docInfo.Params)
                    {
                        var p = new Parameter();
                        meth.Parameters.Add(p);
                        p.Name = param.Attribute("name").Value;
                        p.Description = param.Value;
                    }
                }
            }
        }
    }
}

Note that this won’t compile for you because it references a custom attribute, ApiMethodAttribute, and my base class, BaseController. However, you could delete the logic around these and the documentation should still generate.

Now it’s just a matter of calling the class. I use mine in an MVC ActionResult:

/// <summary>
/// Uses reflection to document our API methods
/// </summary>
/// <returns></returns>
public ActionResult APIDocumentation()
{
    var pathToDocs = HttpContext.Server.MapPath("~/bin/APIDocumentation.xml");
    var model = new ApiDocumentationGenerator(pathToDocs);
    model.Generate();
    return View("admin/apidocumentation", model);
}

And for clarity, I’ll include my View, so you can see how it’s used to render the results to the user:

@model Web.Code.ApiDocumentationGenerator
@{
    ViewBag.Title = "API Documentation";
}

<h2>API Documentation</h2>

@foreach (var method in Model.Methods.OrderBy(x => x.Name))
{
    <h3>@method.Name</h3>
    <p><i>@method.Summary</i></p>
    if (method.Parameters.Any())
     {
         <ul>
             @foreach (var param in method.Parameters)
             {
                 <li><strong>@param.Name </strong>@param.Description</li>
             }
         </ul>
     }
}

Hope that helps.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ben Liebert
Architect BlackBall Software
New Zealand New Zealand
No Biography provided
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinmemberBruno Tagliapietra28-Apr-14 1:06 
GeneralNo code download, and embedded snippets are unreadable PinmemberJohn Brett24-Apr-14 4:27 
GeneralRe: No code download, and embedded snippets are unreadable PinmemberMrChrisBarker24-Apr-14 23:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411023.1 | Last Updated 24 Apr 2014
Article Copyright 2014 by Ben Liebert
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid