在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. 如何在Excel中自动创建报告 Excel中自动创建报告的方法

    如何在Excel中自动创建报告 Excel中自动创建报告的方法,Excel工作表中存储的数据,可以使用宏来一键创建报告并导出为不同的文件类型.Excel自带步骤记录器,因此你不需要自己编写宏.小编在这 ...

  2. 如何在SQL Server Reporting Services中自动创建KPI

    关键绩效指标(KPI) (Key Performance Indicator (KPI)) A Key Performance Indicator aka KPI is a metric which ...

  3. Eclipse中自动创建set、get方法

    先创建类的属性 菜单栏:Source - Generate Getters and Setters 勾选需要创建的方法

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

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

  5. Spring Data Jpa 实体类自动创建数据库表失败解决

    先说一下我遇到的这个问题,首先我是通过maven创建了一个spring boot的工程,引入了Spring data jpa,结果实体类创建好之后,运行工程却没有在数据库中自动创建数据表. 找了半天发 ...

  6. 实体类自动创建数据库表失败解决

    先说一下我遇到的这个问题,首先我是通过maven创建了一个spring boot的工程,引入了Spring data jpa,结果实体类创建好之后,运行工程却没有在数据库中自动创建数据表. 找了半天发 ...

  7. docker 是否自动创建主机挂载目录(先看粗体字)

    volume :需要注意的是,与bind mount不同的是,如果volume是空的而container中的目录有内容,那么docker会将container目录中的内容拷贝到volume中,但是如果 ...

  8. aws iam_使用策略哨兵在AWS IAM中自动执行Salesforce云安全性的最小特权

    aws iam 介绍 (Intro) The Salesforce Security Assurance team would like to share a tool that we recentl ...

  9. 如何在驱动代码中中/dev目录内自动创建

    在<linux /dev>文章中指出,一般驱动加载之后,需要根据申请到的字符设备号通过使用mknod命令将相对应使用到的字符驱动挂载到/dev目录内,这套方法使用起来非常麻烦,因为每次挂载 ...

最新文章

  1. Android开发者值得深入思考的几个问题,大厂面经合集
  2. 数学--数论-- HDU6298 Maximum Multiple 打表找规律
  3. 数据库-Oracle【Oracle数据库设置默认表空间问题及Oracle,SQL,MySQL的自增变量设置】...
  4. Java中抽象类和接口的区别?
  5. 新风口?人造肉第一股表现强劲 股价累计上涨近600%
  6. 为什么将iostream :: eof放在循环条件(即`while(!stream.eof())`)内?
  7. uni.request在接口状态码403等还是走success
  8. 视频防泄密安全解决方案
  9. 数据恢复工具(minitool power data recovery 8) v8.8(含64位32位)
  10. leaflet地图鼠标移动画线
  11. linux环境下删除包含特殊字符的文件或目录
  12. 访问网站提示:您未被授权查看该页恢复办法
  13. 今天看了了一下手册,先弄清楚芯片脚功能
  14. 英特尔i5 1240H
  15. 网易云信智码超清转码技术实践
  16. PDF转TXT免费脚本
  17. 【已解决】联想小新14无线图标消失 | 网络适配器有感叹号 | Windows仍在设置此设备的类配置(代码56)的解决方法
  18. Android:玩转ADB命令(ADB命令使用大全)
  19. 为win11家庭中文版开启组策编辑器
  20. asp.net夜话之八:数据绑定控件(上)

热门文章

  1. CodeBlocks集成Objective-C开发 Windows下学习Objective-C
  2. [Object-C语言随笔之二] 《NSLog》常用的打印调试语句与自动排版
  3. 自适应分辨率可扩展二层JS下拉菜单
  4. WebSocket的故事(六)—— Springboot中,实现更灵活的WebSocket
  5. Office 2016 for Mac 15.24已推送至Office Insider慢速更新通道
  6. No Database Selected
  7. netty io.netty.buffer简介
  8. 实用jQuery代码段
  9. 刚刚入手一台G11,发短信是老是出现“发送自HTC手机”字样
  10. 浅谈Java的输入输出流