La découverte de Générique Contrôleurs ASP.NET de Base
Je suis en train de créer un contrôleur générique comme ceci:
[Route("api/[controller]")]
public class OrdersController<T> : Controller where T : IOrder
{
[HttpPost("{orderType}")]
public async Task<IActionResult> Create(
[FromBody] Order<T> order)
{
//....
}
}
J'ai l'intention de l' {orderType} URI segment variable pour contrôler le type générique de la manette. Je suis en train d'expérimenter à la fois une coutume IControllerFactory
et IControllerActivator
, mais rien ne fonctionne. Chaque fois que j'essaie d'envoyer une demande, je reçois une réponse 404. Le code de mon personnalisée contrôleur de l'usine (et activateur) n'est jamais exécutée.
À l'évidence, le problème est que ASP.NET Core attend valide les contrôleurs à la fin, avec le suffixe "Contrôleur", mais mon contrôleur générique à la place, a la (réflexion) suffixe "Contrôleur " 1". Ainsi, l'attribut basé sur les routes, il déclare passent inaperçues.
Dans ASP.NET MVC, au moins dans ses premiers jours, le DefaultControllerFactory
a été responsable pour la découverte de tous les contrôleurs disponibles. Il testé pour le "Contrôleur" suffixe:
Le framework MVC fournit un contrôleur par défaut d'usine (nommée DefaultControllerFactory) qui va de la recherche à travers toutes les assemblées dans un domaine d'application à la recherche de tous types qui implémentent IController et dont le nom se termine par "Contrôleur".
Apparemment, dans ASP.NET de Base, le contrôleur de l'usine n'a plus cette responsabilité. Comme je l'ai dit plus tôt, mon custom contrôleur de l'usine s'exécute pour "normal" des contrôleurs, mais n'est jamais invoquée pour générique contrôleurs. Donc, il y a autre chose, plus tôt dans le processus d'évaluation, qui régit la découverte de contrôleurs.
Personne ne sait ce que "service" de l'interface est responsable de cette découverte? Je ne connais pas la personnalisation de l'interface ou "crochet" point.
Et personne ne sait d'une façon de faire de ASP.NET de Base "dump" les noms de tous les contrôleurs qu'il a découvert? Il serait bon d'écrire un test unitaire qui vérifie que tous les contrôleur de découverte, j'attends est en effet à travailler.
D'ailleurs, si il y a un "crochet" qui permet de contrôleur générique des noms à être découvert, il implique que la route des substitutions doit également être normalisé:
[Route("api/[controller]")]
public class OrdersController<T> : Controller { }
Indépendamment de la valeur pour T
est donné, l' [controller] nom doit rester une simple base-nom générique. En utilisant le code ci-dessus par exemple, le [controller] valeur doit être "aux Ordres". Il ne serait pas "Commandes " 1" ou "OrdersOfSomething".
Note
Ce problème peut également être résolu en déclarant explicitement les types génériques, au lieu de générer au moment de l'exécution:
public class VanityOrdersController : OrdersController<Vanity> { }
public class ExistingOrdersController : OrdersController<Existing> { }
Les travaux ci-dessus, mais il produit des URI des chemins que je n'aime pas:
~/api/VanityOrders
~/api/ExistingOrders
Ce que j'avais réellement voulu, c'était ceci:
~/api/Orders/Vanity
~/api/Orders/Existing
Un autre réglage qui me reçoit l'URI je suis à la recherche de:
[Route("api/Orders/Vanity", Name ="VanityLink")]
public class VanityOrdersController : OrdersController<Vanity> { }
[Route("api/Orders/Existing", Name = "ExistingLink")]
public class ExistingOrdersController : OrdersController<Existing> { }
Cependant, bien que cela semble fonctionner, il n'a pas vraiment répondu à ma question. Je voudrais utiliser mon contrôleur générique directement au moment de l'exécution, plutôt que de manière indirecte (via manuel de codage) au moment de la compilation. Fondamentalement, cela signifie que je dois ASP.NET de Base pour être en mesure de "voir" ou de "découvrir" mon contrôleur générique, malgré le fait que sa réflexion run-time nom ne se termine pas avec le "Contrôleur" suffixe.
Vous devez vous connecter pour publier un commentaire.
Réponse Courte
Mettre en œuvre
IApplicationFeatureProvider<ControllerFeature>
.Question et la Réponse
La
ControllerFeatureProvider
est responsable de cela.Le faire que dans les
ControllerFeatureProvider.IsController(TypeInfo typeInfo)
.Exemple
MyControllerFeatureProvider.cs
Enregistrer au cours du démarrage.
Voici quelques exemple de sortie.
Et voici une démo sur GitHub. Le meilleur de la chance.
Modifier - Ajouter Les Versions
.NET Version
NuGet.Config
.NET CLI
Restaurer, créer et Exécuter
Edit - Notes sur la RC1 vs RC2
Cela pourrait ne pas être possible est RC1, parce que
DefaultControllerTypeProvider.IsController()
est marqué commeinternal
.Ce qui se passe par défaut
Lors de la découverte du contrôleur de processus, votre ouverture d'générique
Controller<T>
classe sera parmi le candidat types. Mais le défaut de mise en œuvre de laIApplicationFeatureProvider<ControllerFeature>
interface,DefaultControllerTypeProvider
, permettra d'éliminer de votreController<T>
car elle exclut toute classe ouverte paramètres génériques.Pourquoi primordial IsController() ne fonctionne pas
De remplacer la valeur par défaut de mise en œuvre de la
IApplicationFeatureProvider<ControllerFeature>
de l'interface, afin de remplacerDefaultControllerTypeProvider.IsController()
, ne fonctionnera pas. Parce que vous n'avez pas réellement le processus de découverte d'accepter votre ouvrir contrôleur générique (Controller<T>
) en tant que contrôleur valide. Il est pas valide contrôleur de soi, et le contrôleur de l'usine ne sais pas comment instancier de toute façon, parce qu'il ne sais pas ce quiT
est censé être.Ce qui doit être fait
1. Générer fermé types de régulateurs
Avant la découverte du contrôleur de processus commence même, vous avez besoin de générer fermé types génériques de votre ouverture d'contrôleur générique, l'utilisation de la réflexion. Ici, avec deux exemples de types d'entité, nommée
Account
etContact
:Nous avons maintenant fermé
TypeInfos
pourController<Account>
etController<Contact>
.2. Les ajouter à une partie d'une application, et de l'inscrire
Demande de pièces sont généralement enroulée autour de CLR assemblées, mais nous pouvons mettre en œuvre une application personnalisée partie de fournir une collection de types générés lors de l'exécution. Nous avons simplement besoin d'avoir à mettre en œuvre le
IApplicationPartTypeProvider
interface. Par conséquent, notre moteur d'exécution généré par le contrôleur de types de saisir le contrôleur de processus de découverte comme n'importe quel autre type serait.L'application personnalisée partie:
Inscription dans MVC services (
Startup.cs
):Tant que votre contrôleur de dérive de la intégré dans
Controller
classe, il n'y a pas besoin de surcharger laIsController
méthode de laControllerFeatureProvider
. Parce que votre contrôleur générique hérite de la[Controller]
attribut deControllerBase
, il pourra être accepté en tant que contrôleur dans le processus de découverte indépendamment de sa un peu bizarre nom ("Controller`1").3. Remplacer le nom du contrôleur dans le modèle d'application
Néanmoins, "Contrôleur " 1" n'est pas un bon nom à des fins de routage. Vous voulez chacun de vos fermé générique contrôleurs indépendants
RouteValues
. Ici, nous allons remplacer le nom du contrôleur avec le type d'entité, pour correspondre à ce qui allait se passer avec deux indépendants "AccountController" et "ContactController" types.Le modèle de convention de l'attribut:
Appliqué à la classe de contrôleur:
Conclusion
Cette solution reste proche de l'ensemble de la ASP.NET architecture de Base et, entre autres choses, vous permettra de garder une visibilité complète de vos contrôleurs via l'API Explorer (pensez à "Swagger").
Il a été testé avec succès avec à la fois classique et basés sur les attributs de routage.
Demande de Fonctionnalité des Fournisseurs d'examiner la demande de pièces et de fournir des fonctionnalités pour les parties. Il y a built-in fonctionnalité de fournisseurs pour la suite MVC caractéristiques:
Fonction des fournisseurs d'hériter de IApplicationFeatureProvider, où T est le type de la fonction. Vous pouvez implémenter votre propre fournisseur de pour tout de la MVC est fonction du type ci-dessus. L'ordre de la fonctionnalité de fournisseurs dans le ApplicationPartManager.FeatureProviders collection peut être important, puisque plus tard, les fournisseurs peuvent réagir aux mesures prises par les précédents fournisseurs.
Par défaut, ASP.NET Core, MVC ignore générique contrôleurs (par exemple, SomeController). Cet exemple utilise un contrôleur de fonction de prestataire qui s'exécute après que le fournisseur par défaut et ajoute contrôleur générique instances pour une liste des types (défini dans EntityTypes.Les Types):
Les types d'entité:
La fonctionnalité de fournisseur est ajouté au Démarrage:
Par défaut, le contrôleur générique les noms utilisés pour le routage de la forme GenericController`1[Widget] au lieu de Widget. L'attribut suivant est utilisé pour modifier le nom de correspondre au type générique utilisé par le contrôleur:
à l'aide de Microsoft.AspNetCore.Mvc.ApplicationModels;
en utilisant le Système;
La GenericController classe:
Exemple: contrôleur Générique fonctionnalité
Pour obtenir une liste de contrôleurs dans la RC2, juste obtenir ApplicationPartManager de DependencyInjection et ce faire: