通用控制器的.Net核心替代控制器路由

joh*_*y 5 5 c# .net-core asp.net-core

我正在编写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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我如何需要找出一种方法可以覆盖控制器的路由。文档中有关此内容的唯一信息显示了如何创建控制器约定,如下所示:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

由于它是在编译时完成的,因此将不起作用。我需要用户能够在启动时覆盖控制器名称,如何实现?

Nic*_*ico 6

根据您的评论和代码,您与实现这一目标的方式几乎相同。请注意,我已经对示例进行了相当多的删减,以便我可以设置一个测试。

假设我有一个基本的通用控制器:

public class GenericController<T> : Controller
    where T: class
{

    public IActionResult Get()
    {
        return Content(typeof(T).FullName);
    }
}
Run Code Online (Sandbox Code Playgroud)

我现在有一个带有 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来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;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,启动是所有魔法发生的地方。基本上我们将 注册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());
    });
}
Run Code Online (Sandbox Code Playgroud)

从我的评论来看,有两件事让我印象深刻。

  1. 我不确定你为什么有 yourGenericControllerNameConvention作为属性?
  2. 您应该隐式地将Controller路由值设置为您的实体类型(而不是类型+名称)。

给定两个实体(EntityA 和 EntityB),控制器的结果是

/Entitya/get/印刷WebApplication11.Infrastructure.EntityA

/Entityb/get/印刷WebApplication11.Infrastructure.EntityB