我正在写一个RestFramework,我试图找出如何允许用户为通用控制器创建自定义名称。我正在注册我的通用控制器:
public class GenericControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
foreach (var entityConfig in _entityConfigurations)
{
var entityType = entityConfig.Type;
var typeName = entityType.Name + "Controller";
if (!feature.Controllers.Any(t => t.Name == typeName))
{
var controllerType = typeof(GenericController<>)
.MakeGenericType(entityType.AsType())
.GetTypeInfo();
//Normally I would expect there to be an overload to configure the controller name
//feature.Controllers.Add(controllerType, entityConfig.ControllerName);
}
}
}
}
我怎么能想出一种可以覆盖控制器路径的方法。文档中有关此内容的唯一信息显示了如何创建控制器约定,如下所示:
public class GenericControllerNameConvention : Attribute, IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
if (controller.ControllerType.GetGenericTypeDefinition() !=
typeof(GenericController<>))
{
return;
}
var entityType = controller.ControllerType.GenericTypeArguments[0];
controller.ControllerName = entityType.Name;
}
}
这不起作用,因为它是在编译时完成的。我需要用户能够在启动时覆盖控制器名称,我该如何实现?
答案 0 :(得分:4)
根据您的评论和代码,您几乎可以实现这一目标。注意我已经减少了相当多的例子,所以我可以设置一个测试。
假设我有一个基本的通用控制器:
public class GenericController<T> : Controller
where T: class
{
public IActionResult Get()
{
return Content(typeof(T).FullName);
}
}
我现在有一个带有Get动作的打字控制器。现在你的大部分代码都是正确的。所以我的功能提供者为(注意我有一个静态的类型数组):
public class GenericControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
foreach (var entityConfig in ControllerEntity.EntityTypes)
{
var entityType = entityConfig;
var typeName = entityType.Name + "Controller";
if (!feature.Controllers.Any(t => t.Name == typeName))
{
var controllerType = typeof(GenericController<>)
.MakeGenericType(entityType)
.GetTypeInfo();
feature.Controllers.Add(controllerType);
}
}
}
}
接下来是IControllerModelConvention
实施。
public class GenericControllerModelConvention : IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
if (!controller.ControllerType.IsGenericType || controller.ControllerType.GetGenericTypeDefinition() != typeof(GenericController<>))
{
return;
}
var entityType = controller.ControllerType.GenericTypeArguments[0];
controller.ControllerName = entityType.Name + "Controller";
controller.RouteValues["Controller"] = entityType.Name;
}
}
最后,创业是所有魔术发生的地方。基本上我们将IControllerModelConvention
注册到MVC约定选项中,然后注册FeatureProvider
。
public void ConfigureServices(IServiceCollection services)
{
var mvcBuilder = services.AddMvc();
mvcBuilder.AddMvcOptions(o => o.Conventions.Add(new GenericControllerModelConvention()));
mvcBuilder.ConfigureApplicationPartManager(c =>
{
c.FeatureProviders.Add(new GenericControllerFeatureProvider());
});
}
从我的评论中,有两件事让我感到震惊。
GenericControllerNameConvention
作为属性?Controller
路由值设置为您的实体类型(而不是类型+名称)。给定两个实体(EntityA和EntityB),控制器的结果是
/Entitya/get/
打印WebApplication11.Infrastructure.EntityA
/Entityb/get/
打印WebApplication11.Infrastructure.EntityB