Irgendwie hatte ich ein paar Tage frei und skizzierte den GraphQL-Server für unsere Docsvision-Plattform. Im Folgenden werde ich Ihnen sagen, wie es gelaufen ist.

Die Docsvision-Plattform enthält viele verschiedene Tools zum Erstellen von Workflow-Systemen. Die Schlüsselkomponente ist jedoch ORM. Es gibt einen Metadaten-Editor, in dem Sie die Struktur der Kartenfelder beschreiben können. Es kann Struktur-, Sammlungs- und Baumabschnitte geben, die darüber hinaus verschachtelt werden können. Im Allgemeinen ist alles kompliziert . Eine Datenbank wird durch Metadaten generiert, und Sie können dann über eine C # -API damit arbeiten. Kurz gesagt - eine ideale Option zum Erstellen eines GraphQL-Servers.
Was sind die Optionen
Ehrlich gesagt gibt es nicht viele Möglichkeiten und sie sind so lala. Ich habe nur zwei Bibliotheken gefunden:
UPD: In den Kommentaren wurde vorgeschlagen, dass es immer noch Hotchocolate gibt .
Auf README mochte ich zuerst die zweite und fing sogar an, etwas damit zu machen. Aber er stellte bald fest, dass ihre API zu schlecht war und sie die Aufgabe, ein Metadatenschema zu generieren, nicht bewältigen konnte. Es scheint jedoch bereits aufgegeben worden zu sein (das letzte Commit vor einem Jahr).
Die graphql-dotnet
API ist recht flexibel, aber gleichzeitig schrecklich dokumentiert, verwirrend und nicht intuitiv. Um zu verstehen, wie man damit arbeitet, musste ich mir den Quellcode ansehen ... 0.17.3
, ich habe mit Version 0.16
, während jetzt die letzte Version 0.17.3
ist und 7 Beta-Versionen 2.0
bereits veröffentlicht wurden. Es tut mir leid, wenn das Material etwas veraltet ist.
Ich muss auch erwähnen, dass Bibliotheken mit nicht signierten Assemblys geliefert werden. Ich musste sie manuell aus der Quelle neu erstellen, um sie in unserer ASP.NET-Anwendung mit signierten Assemblys verwenden zu können.
GraphQL-Serverstruktur
Wenn Sie mit GraphQL nicht vertraut sind, können Sie den Github Explorer ausprobieren. Ein kleines Geheimnis - Sie können Strg + Leertaste drücken, um die automatische Vervollständigung zu erhalten. Der Client-Teil dort ist nichts anderes als GraphiQL , das einfach an Ihren Server angeschraubt werden kann. Nehmen Sie einfach index.html , fügen Sie Skripte aus dem npm-Paket hinzu und ändern Sie die URL in der graphQLFetcher-Funktion in die Adresse Ihres Servers - das ist alles, was Sie spielen können.
Betrachten Sie eine einfache Abfrage:
query { viewer { login, company } }
Hier sehen wir eine Reihe von Feldern - Viewer, darin Login, Firma. Unsere Aufgabe ist es, wie das GraphQL-Backend, auf dem Server ein "Schema" aufzubauen, in dem alle diese Felder verarbeitet werden. Tatsächlich müssen wir nur eine geeignete Struktur von Serviceobjekten mit einer Beschreibung der Felder erstellen und Rückruffunktionen definieren, um die Werte zu berechnen.
Das Schema kann automatisch basierend auf C # -Klassen generiert werden, aber wir werden den Hardcore durchlaufen - wir werden alles mit unseren Händen tun. Dies liegt jedoch nicht daran, dass ich ein schneidiger Typ bin. Das Generieren eines metadatenbasierten Schemas ist ein nicht standardmäßiges Skript in graphql-dotnet, das von der offiziellen Dokumentation nicht unterstützt wird. Also graben wir ein wenig in ihrem Bauch, in einem undokumentierten Bereich.
Nachdem wir das Schema erstellt haben, müssen wir die Anforderungszeichenfolge (und die Parameter) vom Client auf eine bequeme Weise an den Server senden (es spielt keine Rolle, wie GET, POST, SignalR, TCP ...) und die Engine zusammen mit dem Schema einspeisen. Die Engine spuckt ein Objekt aus, mit dem Ergebnis, dass wir es in JSON umwandeln und an den Client zurückgeben. Für mich sah es so aus:
// , var schema = GraphQlService.GetCardsSchema(sessionContext); // ( ) var executer = new DocumentExecuter(); // , var dict = await executer.ExecuteAsync(schema, sessionContext, request.Query, request.MethodName).ConfigureAwait(false); // - :) if (dict.Errors != null && dict.Errors.Count > 0) { throw new InvalidOperationException(dict.Errors.First().Message); } // return Json(dict.Data);
Sie können auf sessionContext
. Dies ist unser Docsvision-spezifisches Objekt, über das auf die Plattform zugegriffen wird. Beim Erstellen eines Schemas arbeiten wir immer mit einem bestimmten Kontext, aber dazu später mehr.
Schaltungserzeugung
Alles beginnt auf rührende Weise:
Schema schema = new Schema();
Leider endet hier der einfache Code. Um dem Schema ein Feld hinzuzufügen, benötigen wir:
- Beschreiben des Typs: Erstellen Sie ein ObjectGraphType-, StringGraphType-, BooleanGraphType-, IdGraphType-, IntGraphType-, DateGraphType- oder FloatGraphType-Objekt.
- Beschreiben Sie das Feld selbst (Name, Handler) - erstellen Sie ein GraphQL.Types.FieldType-Objekt
Lassen Sie uns versuchen, diese einfache Anfrage zu beschreiben, die ich oben zitiert habe. In der Anfrage haben wir einen Feldbetrachter. Um es einer Abfrage hinzuzufügen, müssen Sie zuerst den Typ beschreiben. Der Typ ist einfach - ein Objekt mit zwei Zeichenfolgenfeldern - Login und Firma. Wir beschreiben das Anmeldefeld:
var loginField = new GraphQL.Types.FieldType(); loginField.Name = "login"; loginField.ResolvedType = new StringGraphType(); loginField.Type = typeof(string); loginField.Resolver = new MyViewerLoginResolver();
Wir erstellen das companyField-Objekt auf die gleiche Weise - ausgezeichnet, wir sind bereit, den Typ des Viewer-Felds zu beschreiben.
ObjectGraphType<UserInfo> viewerType = new ObjectGraphType<UserInfo>(); viewerType.Name = "Viewer"; viewerType.AddField(loginField); viewerType.AddField(companyField);
Es gibt einen Typ, jetzt können wir das Viewer-Feld selbst beschreiben:
var viewerField = new GraphQL.Types.FieldType(); viewerField.Name = "viewer"; viewerField.ResolvedType = viewerType; viewerField.Type = typeof(UserInfo); viewerField.Resolver = new MyViewerResolver();
Nun, und zum Schluss fügen Sie unser Feld zum Abfragetyp hinzu:
var queryType = new ObjectGraphType(); queryType.AddField(viewerField); schema.Query = queryType;
Das ist alles, unser Plan ist fertig.
Sammlungen, Paginierung, Parameterverarbeitung
Wenn das Feld nicht ein Objekt, sondern eine Sammlung zurückgibt, müssen Sie dies explizit angeben. Umschließen Sie dazu einfach den Eigenschaftstyp in eine Instanz der ListGraphType-Klasse. Angenommen, wenn ein Betrachter eine Sammlung zurückgibt, schreiben wir einfach Folgendes:
Dementsprechend wäre es im MyViewerResolver-Resolver erforderlich, die Liste zurückzugeben.
Wenn Sammlungsfelder angezeigt werden, ist es wichtig, das Paging sofort durchzuführen. Hier gibt es keinen vorgefertigten Mechanismus, alles wird über die Parameter erledigt. Sie könnten ein Beispiel für die Verwendung des Parameters im obigen Beispiel bemerken (cardDocument hat einen ID-Parameter). Fügen wir dem Viewer einen solchen Parameter hinzu:
var idArgument = new QueryArgument(typeof(IdGraphType)); idArgument.Name = "id"; idArgument.ResolvedType = new IdGraphType(); idArgument.DefaultValue = Guid.Empty; viewerField.Arguments = new QueryArguments(idArgument);
Dann können Sie den Parameterwert im Resolver wie folgt abrufen:
public object Resolve(ResolveFieldContext context) { var idArgStr = context.Arguments?["id"].ToString() ?? Guid.Empty.ToString(); var idArg = Guid.Parse(idArgStr);
GraphQL ist so typisiert, dass Guid natürlich nicht analysieren konnte. Okay, es ist nicht schwer für uns.
Docsvision Card Request
Bei der Implementierung von GrapqhQL für die Docsvision-Plattform gehe ich dementsprechend einfach den Metadatencode ( sessionContext.Session.CardManager.CardTypes
) durch und erstelle für alle Karten und deren Abschnitte automatisch solche Objekte mit den entsprechenden Resolvern. Das Ergebnis ist ungefähr so:
query { cardDocument(id: "{AF652E55-7BCF-E711-8308-54A05079B7BF}") { mainInfo { name instanceID } } }
Hier ist cardDocument der Kartentyp, mainInfo ist der Name des Abschnitts darin, name und instanceID sind die Felder im Abschnitt. Die entsprechenden Resolver für Karte, Abschnitt und Feld verwenden die CardManager-API wie folgt:
class CardDataResolver : GraphQL.Resolvers.IFieldResolver { public object Resolve(ResolveFieldContext context) { var sessionContext = (context.Source as SessionContext); var idArg = Guid.Parse(context.Arguments?["id"].ToString() ?? Guid.Empty.ToString()); return sessionContext.Session.CardManager.GetCardData(idArg); } } class SectionResolver : GraphQL.Resolvers.IFieldResolver { CardSection section; public SectionFieldResolver(CardSection section) { this.section = section; } public object Resolve(ResolveFieldContext context) { var idArg = Guid.Parse(context.Arguments?["id"].ToString() ?? Guid.Empty.ToString()); var skipArg = (int?)context.Arguments?["skip"] ?? 0; var takeArg = (int?)context.Arguments?["take"] ?? 15; var sectionData = (context.Source as CardData).Sections[section.Id]; return idArg == Guid.Empty ? sectionData.GetAllRows().Skip(skipArg).Take(takeArg) : new List<RowData> { sectionData.GetRow(idArg) }; } } class RowFieldResolver : GraphQL.Resolvers.IFieldResolver { Field field; public RowFieldResolver(Field field) { this.field = field; } public object Resolve(ResolveFieldContext context) { return (context.Source as RowData)[field.Alias]; } }
Natürlich können Sie hier nur Karten nach ID anfordern, aber es ist einfach, auf die gleiche Weise ein Schema für den Zugriff auf erweiterte Berichte, Dienste und alles andere zu erstellen. Mit dieser API können Sie alle Daten aus der Docsvision-Datenbank abrufen, indem Sie einfach das entsprechende JavaScript schreiben. Dies ist sehr praktisch, um Ihre eigenen Skripte und Erweiterungen zu schreiben.
Fazit
Mit GrapqhQL in .NET sind die Dinge nicht einfach. Es gibt eine etwas lebhafte Bibliothek ohne einen zuverlässigen Anbieter und mit einer unverständlichen Zukunft, eine instabile und seltsame API, eine unbekannte Art und Weise, wie sie sich unter Last verhält und wie stabil sie ist. Aber wir haben das, was wir haben, es scheint zu funktionieren, aber die Mängel in der Dokumentation und im Rest werden durch die Offenheit des Quellcodes ausgeglichen.
Was ich in diesem Artikel beschrieben habe, ist eine zunehmend undokumentierte API, die ich durch Eingabe und Untersuchung der Quelle untersucht habe. Es ist nur so, dass die Autoren der Bibliothek nicht dachten, dass jemand die Schaltung automatisch generieren müsste - nun, was können Sie tun, dies ist Open Source.
Es wurde das alles über das Wochenende geschrieben und für sich genommen bisher nur ein Prototyp. Im Standard-Docsvision-Paket wird dies wahrscheinlich angezeigt, aber wann - ist immer noch schwer zu sagen. Wenn Sie jedoch die Idee haben möchten, direkt von JavaScrpit aus auf die Docsvision-Datenbank zuzugreifen, ohne Servererweiterungen zu schreiben, schreiben Sie. Je höher das Interesse der Partner ist, desto mehr Aufmerksamkeit werden wir diesem Thema widmen.