基于Oracle的EntityFramework的WEBAPI2的实现(四)——自动生成在线帮助文档

打开我们项目中的Area文件夹,正常情况下,我们会发现已经有了一个名字叫做【HelpPage】的区域(Area),这个区域是vs帮助我们自动建立的,它是一个mvc(不是webapi),它有普通的Conroller和Action,以及View,Model等。我们可以在调试的时候输入地址:http://localhost:8080/Help,就可以看见一个页面,有没有看见自己熟悉的TestController(名字中去掉了Controller,只剩下Test了)。如果没有,那么,一定是我们的配置有问题,那么我们下面的检查一下。

首先,要达到自动生成在线帮助的页面,我们必须提示说明信息。其实我们的说明信息就是我们对我们的Controller类的注释以及每一个方法的注释。当然不是//这样的注释,而是如下的注释:

///<summary>

///这才是真的注释

///</summary>

这个注释放在我们的类和我们的Controller以及Controller下的各种Action上,然后把文字写清楚了。右击我们的项目·属性,然后设置如下:

image

在【生成】标签页里面设置好【XML文档文件】,在里面写好地址,应该和我们的项目的dll在一个位置(通常是主项目下的bin目录),然后全部保存。

这个时候我们所有对类和成员的注释在生成的时候都会被写入进这个xml文件中,但是,我们的在线帮助页要如何取得这些数据呢,当然,这就需要我们告诉它我们的这个xml文件在什么位置。先来看一下目录图。

image

我们在App_Start文件夹里面找到【HelpPageConfig.cs】文件,这个文件是整个 HelpPage的配置所在。将此文件打开,找到如下的代码,解开注释,并修改为如下内容:

config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/WebApi.xml")));

此时,我们按照一开始时调试 一下,应该就可以看到在每个ApiController上的注释了。但是,我们发现,我们的Model类后面的注释都是空的,原因是我们的Model类是从数据库生成的,虽然数据库里面有注释,EF并没有帮我们把注释从数据库中取进来,有一些其它的第三方工具,可以实现此功能,但是事实上,数据库的注释是给我们看的,而我们的Model类的注释是给调用者看的,这是不一样的,比如我们的Model类型需要写明这个属性是否可以为空,而数据库中的注释有时候会泄露我们系统业务的内部秘密。所以,这个时候,我们打开我们的Model设计器(双击那个edmx文件)选择一个类的一个属性,右击,属性,就可以看见属性里面有一项叫文档,点开,在摘要里面写上我们的注释,即可。当然,详细内容也可以写(但是对HelpPage不做修改的情况下,是看不见详细内容的)。

这还不够,因为edmx只是生成映射文件,是数据库表与我们的类的映射,当然它还包含了一些我们自定义的信息(如刚刚的注释),但是,真正担任代码生成工作的是我们的.tt文件(T4代码模板?T4代码生成代码?随便你怎么叫)。我们点开edmx文件前面的三角号展开它,看见有一个与edmx文件同名但是扩展名为.tt的文件,双击打开它(等等,不要慌,这不是乱码,这是T4的代码,如果有高亮的话,你会觉得它和ASPX或者CSHTML一样让人喜爱,只是现在这个样子确实有点丑,丑得让人看不懂,不过没关系,大家通过无下划线的上下文搜索是可以搜索到相关位置的,然后加入有下划线的代码即可)。如下:

WriteHeader(codeStringGenerator, fileManager);
string summary=string.Empty;
foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
{
    fileManager.StartNewFile(entity.Name + ".cs");
    BeginNamespace(code);
    if(entity.Documentation != null && entity.Documentation.Summary != null)
    {
        summary=entity.Documentation.Summary;
    }else{
        summary=entity.Name;
    }
#>
<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
///<summary>
///<#=summary#>
///</summary>
<#=codeStringGenerator.EntityClassOpening(entity)#>
{
var simpleProperties = typeMapper.GetSimpleProperties(entity);
    if (simpleProperties.Any())
    {
        foreach (var edmProperty in simpleProperties)
        {
            if(edmProperty.Documentation !=null && edmProperty.Documentation.Summary != null)
            {
                summary=edmProperty.Documentation.Summary;
            }else
            {
                summary="";
            }
#>
    ///<summary>
    ///<#=summary#>
    ///</summary>
    <#=codeStringGenerator.Property(edmProperty)#>

当我们部分注释是源于其它项目时,该怎么办,比如Model是单独的项目

那么问题来了,我们刚刚只讲到让HelpPage只取一个WebApi.xml,但是,如果我们的Model在WebApi.Model这个项目中怎么办。我可以告诉你,如果在WebApi.Model中,那么你写一万行的注释,HelpPage也不会让你看见的,因为你的WebApi.Model.Xml并没有给它,它取不到里面的信息。

那么我们右击WebApi.Model这个项目,点属性,然后在【生成】中将【XML文档生成】的位置设置到我们的WebApi项目的bin目录下,与WebApi.xml在 一起。

ok,还有下面一步,就是在HelpPageConfing.cs中,告诉系统我们xml的位置。这个时候,你会发现,【XmlDocumentationProvider】这个类很贱的没有两个参数的重载~~我操!!

那我们现在有两个xml文件,该怎么办?我们在上面的那个目录结构图中,发现,这个【XmlDocumentationProvider】类的源码就在那里,是不是很兴奋,有想改这个类的冲动?别吧,留着它,万一以后用得着呢?我们照着它新建一个继承IDocumentationProvider, IModelDocumentationProvider的类,名字叫【MultiXmlDocumentationProvider】(这个类是我很久之前从网上找的,不知道作者是谁了,在此表示深深的感谢),它的实现如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Description;
using WebApi.Areas.HelpPage.ModelDescriptions;

namespace WebApi.Areas.HelpPage
{
    /// <summary>A custom <see cref="IDocumentationProvider"/> that reads the API documentation from a collection of XML documentation files.</summary>  
    public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
    {
        /********* 
        ** Properties 
        *********/
        /// <summary>The internal documentation providers for specific files.</summary>  
        private readonly XmlDocumentationProvider[] Providers;


        /********* 
        ** Public methods 
        *********/
        /// <summary>Construct an instance.</summary>  
        /// <param name="paths">The physical paths to the XML documents.</param>  
        public MultiXmlDocumentationProvider(params string[] paths)
        {
            this.Providers = paths.Select(p => new XmlDocumentationProvider(p)).ToArray();
        }

        /// <summary>Gets the documentation for a subject.</summary>  
        /// <param name="subject">The subject to document.</param>  
        public string GetDocumentation(MemberInfo subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>  
        /// <param name="subject">The subject to document.</param>  
        public string GetDocumentation(Type subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>  
        /// <param name="subject">The subject to document.</param>  
        public string GetDocumentation(HttpControllerDescriptor subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>  
        /// <param name="subject">The subject to document.</param>  
        public string GetDocumentation(HttpActionDescriptor subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>  
        /// <param name="subject">The subject to document.</param>  
        public string GetDocumentation(HttpParameterDescriptor subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }

        /// <summary>Gets the documentation for a subject.</summary>  
        /// <param name="subject">The subject to document.</param>  
        public string GetResponseDocumentation(HttpActionDescriptor subject)
        {
            return this.GetFirstMatch(p => p.GetDocumentation(subject));
        }


        /********* 
        ** Private methods 
        *********/
        /// <summary>Get the first valid result from the collection of XML documentation providers.</summary>  
        /// <param name="expr">The method to invoke.</param>  
        private string GetFirstMatch(Func<XmlDocumentationProvider, string> expr)
        {
            return this.Providers
                .Select(expr)
                .FirstOrDefault(p => !String.IsNullOrWhiteSpace(p));
        }
    }
}

这个时候,我们再回到HelpPageConfig.cs类中,找到刚刚那句:

config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/WebApi.xml")));

将它替换为:

config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/WebApi.xml"), HttpContext.Current.Server.MapPath("~/bin/WebApi.Model.XML")));

这时候,我们再调试一下,倍儿爽!!~~~~

posted @ 2015-10-23 16:26  ensleep  阅读(1243)  评论(0编辑  收藏  举报