在MIS系统中,大部分的操作都是基本的CRUD,并且这样的Controller非常多。

为了复用代码,我们常常写一个泛型的基类。

public class EntityController<T> : ApiController
    {
        public
IQueryable<T> GetAll()
        {
            ...
        }

public
T Get(int id)
        {
            ...
        }

public
T Put(int id, Ink ink)
        {
            ...
        }

public
T Post(Ink ink)
        {
            ...
        }

public
void Delete(int id)
        {
            ...
        }
    }

当增加一种类型的时候,我们需要手动增加一个子类:

public
class
InkController : EntityController<Ink>
    {
    }

public
class
PenController : EntityController<Pen>
    {
    }

当实体模型比较多的时候仍然就存在繁琐和难以维护的问题。因此我们也需要一种自动创建的机制,

要实现自动创建Controller,首先得把现在这种静态创建Controller的方式改成动态创建的方式。在WebAPI中,我们可以通过替换IHttpControllerSelector来实现动态创建Controller。

首先我们实现自己的IHttpControllerSelector。

public
class
MyControllerSelector : DefaultHttpControllerSelector
    {
        private
readonly
HttpConfiguration _configuration;

public MyControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
            _configuration = configuration;
        }

public
override
HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            var controllerName = base.GetControllerName(request);
            return
new
HttpControllerDescriptor(_configuration, controllerName, typeof(Controllers.EntityController<Ink>));
        }
    }

然后在初始化函数中注册我们的ControllerSelector

public
static
class
WebApiConfig
{
public
static
void Register(HttpConfiguration config)
{
            // Web API configuration and services
            GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new
MyControllerSelector(GlobalConfiguration.Configuration));

// Web API routes
            config.MapHttpAttributeRoutes();
            ……
}
}

这样一来,即使没有子类InkController,我们同样可以特化泛型控制器EntityController<Ink>实现同样的效果。

到这一步后,还存在一个问题:ControllerSelector只能根据HttpRequest获取ControllerName,并不知道其对应的Model,不知道该如何特化EntityController。解决这个问题的常见的方式就是维护一张Controller名称和实体类型的映射表:

public
class
MyControllerSelector : DefaultHttpControllerSelector
    {
        static
Dictionary<string, Type> _entityMap;
        static MyControllerSelector()
        {
            _entityMap = new
Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);

_entityMap["Ink"] = typeof(Ink);
            _entityMap["Pen"] = typeof(Pen);
        }

private
readonly
HttpConfiguration _configuration;

public MyControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
            _configuration = configuration;
        }

public
override
HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            var controllerName = base.GetControllerName(request);

Type entityType = null;
            if (!_entityMap.TryGetValue(controllerName.ToLower(), out entityType))
            {
                return
base.SelectController(request);
            }

return
new
HttpControllerDescriptor(_configuration, controllerName, typeof(Controllers.EntityController<>).MakeGenericType(entityType));
        }
    }

虽然这样做本身没有什么问题。这种手动维护Controller列表的方式仍然无法达到自动创建Controller的要求,因此我们还需要一种自动生成这种映射表的机制。这里我仍然是采用同前文一样的Attribute+反射的方式。

首先写一个ControllerAttribute,

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    public
class
ControllerAttribute : Attribute
    {
        public
string Name { get; private
set; }

public ControllerAttribute(string name)
        {
            this.Name = name;
        }
    }

然后,在数据模型中标记该Attribute

[Controller("Pen")]
    public
class
Pen

[Controller("Ink")]
    public
class
Ink

最后,根据反射建立Controller名称和类型的关联关系

static
Dictionary<string, Type> _entityMap;
    static MyControllerSelector()
    {
        var assembly = typeof(MyControllerSelector).Assembly;

var entityTypes = from type in assembly.GetTypes()
                            let controllerAtt = type.GetCustomAttribute<ControllerAttribute>()
                            where controllerAtt != null
                            select
new { Type = type, ControllerName = controllerAtt.Name };

_entityMap = entityTypes.ToDictionary(i => i.ControllerName, i => i.Type, StringComparer.OrdinalIgnoreCase);
    }

这样基本上就可以用了。最后顺手做一下优化,减少的HttpControllerDescriptor创建操作,最终版本的ControllerSelector如下。

public
class
MyControllerSelector : DefaultHttpControllerSelector
    {
        private
Dictionary<string, HttpControllerDescriptor> _controllerMap;

public MyControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
            var entityTypes = from type in
typeof(MyControllerSelector).Assembly.GetTypes()
                             let controllerAtt = type.GetCustomAttribute<ControllerAttribute>()
                             where controllerAtt != null
                             select
new { Type = type, ControllerName = controllerAtt.Name };

_controllerMap = entityTypes.ToDictionary(
                                i => i.ControllerName,
                                i => new
HttpControllerDescriptor(configuration, i.ControllerName,
                                        typeof(Controllers.EntityController<>).MakeGenericType(i.Type)),
                                StringComparer.OrdinalIgnoreCase);
        }

public
override
HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            HttpControllerDescriptor controllerDescriptor = null;
            if (!_controllerMap.TryGetValue(base.GetControllerName(request), out controllerDescriptor))
            {
                return
base.SelectController(request);
            }
            else
            {
                return controllerDescriptor;
            }
        }
    }

在WebAPI中自动创建Controller的更多相关文章

  1. 在Code First中自动创建Entity模型

    之前我在博客文章中介绍过如何使用Code First来创建数据库,通过CodeFirst的方式,可以大幅的减少开发人员的工作量,比传统的先创建库再ORM的方式简单了不少.但是,很多时候,特别是一些MI ...

  2. ExtJS6的中sencha cmd中自动创建案例项目代码分析

    在之前的博文中,我们按照sencha cmd的指点,在自己win7虚拟机上创建了一个案例项目,相当于创建了一个固定格式的文档目录结构,然后里面自动创建了一系列js代码.这是使用sencha cmd自动 ...

  3. Xcode6中如何修改文件中自动创建的Created by和Copyright

    转自: http://blog.csdn.net/bjourney/article/details/46832159 在Xcode6创建问的时候,会自动生成注释 //  Created byxxx o ...

  4. 在 Xcode中 修改文件中自动创建的Created by和Copyright

    在Xcode里创建的时候,会自动生成注释 //  Created byxxx on 15/7/10. //  Copyright (c) 2015年 xxxx. All rights reserved ...

  5. 浅谈ETL架构中ODS的作用以及如何在HaoheDI中自动创建ODS表

    什么是ODS表? 在ETL架构中,源数据很少会直接抽取加载到数据仓库EDW,二者之间往往会设置一个源数据的临时存储区域,存储数据在清洗转换前的原始形态,通常被大家称做操作型数据存储,简称ODS,在Ki ...

  6. 013_使用 user.txt 文件中的人员名单,在计算机中自动创建对应的账户并配置初始密码

    for i in `cat user.txt`do    useradd $i    echo "123456" | passwd --stdin $idone

  7. 转载:性能优化——统计信息——SQLServer自动更新和自动创建统计信息选项

    这段时间AX查询变得非常慢,每天都有很多锁. 最后发现是数据库统计信息需要更新. ----------------------------------------------------------- ...

  8. 性能优化——统计信息——SQLServer自动更新和自动创建统计信息选项

    原文:性能优化--统计信息--SQLServer自动更新和自动创建统计信息选项 原文译自:http://www.mssqltips.com/sqlservertip/2766/sql-server-a ...

  9. [.NET] WebApi 生成帮助文档及顺便自动创建简单的测试工具

    ==========最终的效果图========== ==========下面开始干活:生成帮助文档========== 一.创建 WebApi 项目 二.找到 HelpPageConfig.cs 并 ...

随机推荐

  1. 数据库最大连接池max pool size

    本文导读:Max Pool Size如果未设置则默认为100,理论最大值为32767.最大连接数是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影 ...

  2. 初探groupcache

    groupcache是用于dl.google.com的一个memcached的替代品,相对于memcached,提供更小的功能集和更高的效率,以第三方库的形式提供服务. groupcache的常见部署 ...

  3. 阿里 RocketMQ 安装与简介

    一.简介 官方简介: l  RocketMQ是一款分布式.队列模型的消息中间件,具有以下特点: l  能够保证严格的消息顺序 l  提供丰富的消息拉取模式 l  高效的订阅者水平扩展能力 l  实时的 ...

  4. 不同操作系统上屏蔽oracle的操作系统认证方式

    windows系统上>如果不想用户通过操作系统验证方式登录,可以修改 sqlnet.ora文件,把 SQLNET.AUTHENTICATION_SERVICES=NTS 前面加#注释掉就可以了. ...

  5. bootstrap模态框Esc键不关闭

    项目开发时很多时候会需要用到弹出框,而且很多框架都有自己的弹出框,比较现在很流行的bootstrap就有模态框(model). 很多时候这东西用起来非常方便,可以为开发省去很多自己定义的时间!最近项目 ...

  6. mysql的ONLY_FULL_GROUP_BY语义 --转自http://www.wtoutiao.com/p/19dh3ec.html

    执行SET GLOBAL sql_mode = ''; 把sql_mode 改成非only_full_group_by模式.验证是否生效 SELECT @@GLOBAL.sql_mode 或 SELE ...

  7. SSIS 部署到SQL Job

    微软 BI 系列随笔 - SSIS 基础 - 部署SQL Job 简介 在之前博客中,讲述了如何实现SSIS的项目部署以及利用SSIS的参数与环境加速部署,参见 微软 BI 系列随笔 - SSIS 基 ...

  8. plsql登录找不到可连接数据库

    环境: OS:server 2008r2 64位 现象: plsql安装完成后,登录时数据库下拉列表为空,但主目录和oci库都能正常检测到. 原因: 系统环境变量未设置. 解决: 设置系统环境变量.t ...

  9. ERROR 1130 (HY000):Host'localhost'解决方法

    http://www.2cto.com/database/201211/169504.html ERROR 1130 (HY000):Host'localhost'解决方法   ERROR 1130 ...

  10. python--ulipad控制台中文输出乱码

    ulipad用起来顺手,而不尽人意的地方时,它不能正确输出中文.而且有人指出这和文件的编码没关系,所以将”设置“选项里”缺省文档编码“修改为”utf-8“也无济于事.为了解决这个问题,我在网上搜了搜, ...