您的位置:首页 > 其它

在WebAPI中自动创建Controller

2016-02-10 10:13 447 查看
在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;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: