Ermitteln generischer Controller in ASP.NET Core

8

Ich versuche, einen generischen Controller wie folgt zu erstellen:

%Vor%

Ich beabsichtige, dass die URI-Segmentvariable {orderType} den generischen Typ des Controllers steuert. Ich experimentiere mit einem benutzerdefinierten IControllerFactory und IControllerActivator , aber nichts funktioniert. Jedes Mal, wenn ich versuche, eine Anfrage zu senden, erhalte ich eine 404-Antwort. Der Code für meine benutzerdefinierte Controller-Factory (und den Aktivator) wird nie ausgeführt.

Offensichtlich besteht das Problem darin, dass ASP.NET Core erwartet, dass gültige Controller mit dem Suffix "Controller" enden, aber mein generischer Controller hat stattdessen das (reflektionsbasierte) Suffix "Controller1". Daher bleiben die von ihm deklarierten attributbasierten Routen unbemerkt.

In ASP.NET MVC war % ce_de% zumindest in den frühen Tagen für das Entdecken verantwortlich alle verfügbaren Controller . Es wurde auf das Suffix "Controller" getestet:

  

Das MVC-Framework stellt eine Standard-Controller-Factory (passenderweise namens DefaultControllerFactory) zur Verfügung, die alle Assemblys in einer App-Domäne nach allen Typen durchsucht, die IController implementieren und deren Name mit "Controller" endet.

Offenbar hat die Controller Factory in ASP.NET Core diese Verantwortung nicht mehr. Wie bereits erwähnt, wird meine benutzerdefinierte Controller-Factory für "normale" Controller ausgeführt, aber nie für generische Controller aufgerufen. Es gibt also noch etwas anderes im Evaluierungsprozess, das die Entdeckung von Controllern regelt.

Weiß jemand, welche "Service" -Schnittstelle für diese Entdeckung verantwortlich ist? Ich kenne die Anpassungsschnittstelle oder den "Haken" -Punkt nicht.

Und weiß jemand von einer Möglichkeit, ASP.NET Core die Namen aller gefundenen Controller "dump" zu machen? Es wäre großartig, einen Komponententest zu schreiben, der überprüft, ob eine benutzerdefinierte Controller-Erkennung, die ich erwarte, tatsächlich funktioniert.

Wenn übrigens ein "Hook" vorhanden ist, der es erlaubt, generische Controller-Namen zu entdecken, bedeutet dies, dass Routensubstitutionen ebenfalls normalisiert werden müssen:

%Vor%

Unabhängig davon, welcher Wert für DefaultControllerFactory angegeben ist, der Name des [controller] muss ein einfacher Basis-generischer Name bleiben. Unter Verwendung des obigen Codes als Beispiel wäre der [Controller] -Wert "Aufträge". Es wäre nicht "Orders'1" oder "OrdersOfSomething".

Hinweis

Dieses Problem könnte auch dadurch gelöst werden, dass die generischen Typen mit geschlossenem Typ explizit deklariert werden, anstatt sie zur Laufzeit zu generieren:

%Vor%

Das obige funktioniert, aber es erzeugt URI-Pfade, die ich nicht mag:

%Vor%

Was ich eigentlich wollte, war das:

%Vor%

Eine weitere Anpassung bringt mir die URIs, nach denen ich suche:

%Vor%

Obwohl dies scheint zu funktionieren, beantwortet es meine Frage nicht wirklich. Ich möchte meinen generischen Controller direkt zur Laufzeit verwenden und nicht indirekt (über manuelle Codierung) zur Kompilierungszeit. Im Grunde bedeutet dies, dass ich ASP.NET Core brauche, um meinen generischen Controller "sehen" oder "entdecken" zu können, obwohl sein Laufzeit-Reflektionsname nicht mit dem erwarteten "Controller" -Suffix endet.

    
Brent Arias 17.04.2016, 19:05
quelle

4 Antworten

8

Kurze Antwort

Implementieren Sie IApplicationFeatureProvider<ControllerFeature> .

Frage und Antwort

  

Weiß jemand, welche "Service" -Schnittstelle für [das Auffinden aller verfügbaren Controller] verantwortlich ist?

Die ControllerFeatureProvider ist dafür verantwortlich.

  

Und weiß jemand eine Möglichkeit, ASP.NET Core die Namen aller gefundenen Controller "dump" zu machen?

Machen Sie das innerhalb von ControllerFeatureProvider.IsController(TypeInfo typeInfo) .

Beispiel

MyControllerFeatureProvider.cs

%Vor%

Registrieren Sie es beim Start.

%Vor%

Hier ist eine Beispielausgabe.

%Vor%

Und hier ist eine Demo auf GitHub . Viel Glück.

Bearbeiten - Hinzufügen von Versionen

.NET Version

%Vor%

NuGet.Config

%Vor%

.NET CLI

%Vor%

Wiederherstellen, Erstellen und Ausführen

%Vor%

Bearbeiten - Hinweise zu RC1 vs RC2

Dies ist möglicherweise nicht möglich, ist RC1, weil DefaultControllerTypeProvider.IsController() ist als internal markiert.

    
Shaun Luttin 18.04.2016, 01:32
quelle
3

Was passiert standardmäßig

Während des Controller-Erkennungsprozesses gehört Ihre offene generische Controller<T> -Klasse zu den möglichen Typen. Aber die Standardimplementierung der IApplicationFeatureProvider<ControllerFeature> -Schnittstelle, DefaultControllerTypeProvider , wird Ihre Controller<T> eliminieren, da sie jede Klasse mit offenen generischen Parametern ausschließt.

Warum das Überschreiben von IsController () nicht funktioniert

Das Ersetzen der Standardimplementierung der IApplicationFeatureProvider<ControllerFeature> -Schnittstelle, um DefaultControllerTypeProvider.IsController() zu überschreiben, funktioniert nicht. Weil Sie nicht möchten, dass der Erkennungsprozess Ihren offenen generischen Controller ( Controller<T> ) als gültigen Controller akzeptiert. Es ist nicht ein gültiger Controller an sich, und die Controller Factory würde sowieso nicht wissen, wie sie instanziiert wird, weil sie nicht wissen würde, was T sein soll.

Was zu tun ist

1. Erzeuge geschlossene Controller-Typen

Bevor der Controller-Erkennungsprozess überhaupt gestartet wird, müssen Sie geschlossene generische Typen von Ihrem offenen generischen Controller mithilfe von Reflektion generieren. Hier mit zwei Beispiel-Entitätstypen namens Account und Contact :

%Vor%

Wir haben nun TypeInfos für Controller<Account> und Controller<Contact> geschlossen.

2. Fügen Sie sie einem Anwendungsteil hinzu und registrieren Sie sie

Anwendungsteile werden normalerweise um CLR-Assemblies gewickelt, aber wir können einen benutzerdefinierten Anwendungsteil implementieren, der eine Sammlung von Typen bereitstellt, die zur Laufzeit generiert werden. Wir müssen einfach die Schnittstelle IApplicationPartTypeProvider implementieren. Daher werden unsere von der Laufzeit generierten Controller-Typen wie alle anderen integrierten Typen in den Controller-Erkennungsprozess eintreten.

Der benutzerdefinierte Anwendungsteil:

%Vor%

Registrierung in MVC-Diensten ( Startup.cs ):

%Vor%

Solange Ihr Controller von der integrierten Klasse Controller abgeleitet ist, müssen Sie die Methode IsController des ControllerFeatureProvider nicht wirklich überschreiben. Da Ihr generischer Controller das [Controller] -Attribut von ControllerBase erbt, wird er unabhängig vom etwas bizarren Namen ("Controller1") als Controller im Erkennungsprozess akzeptiert.

3. Überschreiben Sie den Controllernamen im Anwendungsmodell

Dennoch ist "Controller 1" kein guter Name für Routing-Zwecke. Sie möchten, dass jeder Ihrer geschlossenen generischen Controller über unabhängige RouteValues verfügt. Hier werden wir den Namen des Controllers durch den des Entity-Typs ersetzen, damit er mit den beiden unabhängigen Typen "AccountController" und "ContactController" übereinstimmt.

Das Modellkonvention-Attribut:

%Vor%

Angewandt auf die Controller-Klasse:

%Vor%

Fazit

Diese Lösung bleibt der allgemeinen ASP.NET Core-Architektur nahe und unter anderem behalten Sie die volle Transparenz Ihrer Controller über den API Explorer (denken Sie an "Swagger").

Es wurde erfolgreich mit konventionellem und attributbasiertem Routing getestet.

    
Mathieu Renda 15.08.2017 17:17
quelle
0

Um eine Liste von Controllern in RC2 zu erhalten, rufen Sie einfach ApplicationPartManager von DependencyInjection und tun dies:

%Vor%     
bang 13.06.2016 12:27
quelle
0

Anwendungsfunktionsanbieter untersuchen Anwendungsteile und stellen Funktionen für diese Teile bereit. Es gibt integrierte Feature-Provider für die folgenden MVC-Features:

  • Controller
  • Metadatenreferenz
  • Tag-Helfer
  • Komponenten anzeigen

Feature-Provider erben von IApplicationFeatureProvider, wobei T der Typ des Features ist. Sie können eigene Feature-Provider für alle oben aufgeführten Feature-Typen von MVC implementieren. Die Reihenfolge der Feature-Provider in der ApplicationPartManager.FeatureProviders-Auflistung kann wichtig sein, da spätere Provider auf Aktionen früherer Provider reagieren können.

Standardmäßig ignoriert ASP.NET Core MVC generische Controller (z. B. SomeController). In diesem Beispiel wird ein Controller-Feature-Provider verwendet, der nach dem Standardanbieter ausgeführt wird und generische Controller-Instanzen für eine bestimmte Liste von Typen hinzufügt (in EntityTypes.Types definiert):

%Vor%

Die Entitätstypen:

%Vor%

Der Feature-Provider wurde unter Start hinzugefügt:

%Vor%

Standardmäßig haben die generischen Controller-Namen, die für das Routing verwendet werden, die Form GenericController'1 [Widget] anstelle von Widget. Das folgende Attribut wird verwendet, um den Namen so zu ändern, dass er dem generischen Typ entspricht, der vom Controller verwendet wird:

unter Verwendung von Microsoft.AspNetCore.Mvc.ApplicationModels; mit System;

%Vor%

Die GenericController-Klasse:

%Vor%

Beispiel: Generic-Controller-Funktion

    
Mohammad Akbari 05.12.2017 13:05
quelle