2014-02-20 18 views
5

Ich habe Probleme mit meinem Web-Anwendungsdesign mit JSON, ASP.NET, Typoskript/Javascript und AngularJS.
JSON Datenstruktur - JSON zu Objekten - Best Practice

Kurz: Ich brauche eine Best-Practice für Daten vom Server an einen Client über JSON senden, den JSON-String auf der Client-Seite mit Objekten zu erstellen.

Ich habe ein WebServerAPI Projekt (ASP.NET) mit der folgenden Struktur:

  • Controller
    • Datacontroller (REST API)
  • Modell
    • A
    • Typ

Die Modellklassen:

public class A { 
    public property int Id {get; set;} 
    public property string Name {get; set;} 
    public property Type Type {get; set;} 
} 

public class Type { 
    public property int Id {get; set;} 
    public property string Name {get; set;} 
} 

ID jeder Modellklasse gehört zu einer ID in einer Datenbanktabelle (Primärschlüssel).

Die Datacontroller Struktur:

public class DataController : ApiController { 
    // ... 
    // GET api/a 
    public IEnumerable<A> Get() 
    { 
     // fetches all As from the database 
     // the facade creates instances of A and Type as required 
     // (if A has a 'Type' the Type will also fetched an set) 
     return facade.GetAll<A>(); 
    } 
    // ... 
} 

Die Methode get() des Datacontroller gibt ein JSON Ergebnis.

Das JSON Ergebnis sieht wie folgt aus:

[ 
    {"Id":1, 
    "Type": {"Id":1, "Name":"name1"} 
    }, 
    {"Id":2, 
    "Type": {"Id":2, "Name":"name2"} 
    }, 
    {"Id":3, 
    "Type": {"Id":1, "Name":"name1"} 
    } 
    {"Id":4, 
    "Type": {"Id":2, "Name":"name2"} 
    }, 
    {"Id":5, 
    "Type": {"Id":2, "Name":"name2"} 
    }, 
] 

Wie Sie in den JSON-Daten einige Wie teilen die gleichen Typen sehen. Obwohl dies JSON ist, frage ich mich, ob dies die beste Methode ist, Daten zu senden.

Wäre es nicht besser sein, so etwas zu senden:

[ 
    {"Id":1, 
    "TypeId": {"Id":1} 
    }, 
    {"Id":2, 
    "TypeId": {"Id":2} 
    }, 
    {"Id":3, 
    "TypeId": {"Id":1} 
    } 
    {"Id":4, 
    "TypeId": {"Id":2} 
    }, 
    {"Id":5, 
    "TypeId": {"Id":2} 
    }, 
] 

So bekommen wir nur die ID des Typs. Dann müssen wir alle verfügbaren Typen anfordern, um festzustellen, welcher Typ in dem entsprechenden As festgelegt werden muss. Was kann schlecht sein? Ich denke, das könnte langsam sein, weil ich zwei Abfragen senden muss.

Eine dritte Option kann alle verfügbaren Typen und die As in demselben JSON-Ergebnis senden.

[ 
    {"Types": 
     [ 
       {"Id":1, "Name":"name1"}, 
       {"Id":2, "Name":"name2"}, 
     ] 
    }, 
    {"As": 
     [ 
      {"Id":1, 
      "TypeId": {"Id":1} 
      }, 
      {"Id":2, 
      "TypeId": {"Id":2} 
      }, 
      {"Id":3, 
      "TypeId": {"Id":1} 
      } 
      {"Id":4, 
      "TypeId": {"Id":2} 
      }, 
      {"Id":5, 
      "TypeId": {"Id":2} 
      } 
     ] 
    } 
] 

Also ich frage mich, ob es eine Best-Practice dafür ist. Das gleiche Objekt (Type) immer wieder als verschachteltes Objekt in A zu versenden erscheint ziemlich "dumm".

Insbesondere, wenn ich die JSON-Zeichenfolge in Typescript-Objekte konvertieren.

Ohne „Speicher/Cache“ Logik schaffe ich das „gleiche“ über das Objekt und immer wieder:

export class A { 
    public Id: number; 
    public Name: string; 
    public Type: Type; 

    public static fromData(data: any): A { 

     var a = new A(); 
     a.Id = data.Id; 
     a.Name = data.Name; 
     a.Type = Type.fromData(data.Type); 

     return a; 
    } 
} 

export class Type { 
     public Id: number; 
     public Name: string; 

     public static fromData(data: any) : Type { 
      var type = new Type(); 
      type.Id = data.Id; 
      type.Name = data.Name; 

      return type; 
     } 
} 

// AngularJS controller 
export class AListCtrl { 
    static $inject = ['$scope', '$http']; 

    public As: A[] = []; 

    constructor(private $scope, private $http) { 
     $scope.AListCtrl = this; 

     $http.get('http://localhost/api/a').success((data) => { 
      var as: A[] = []; 
      for (var i = 0; i < data.length; i++) { 
       var aData = data[i]; 
       var a = A.fromData(aData); 

       as.push(a); 
      } 

      this.As = as; 
     }); 
    } 
} 



verschiedene Objekte erstellen, die die gleichen Typen repräsentieren nur falsch zu sein scheint. Weil ich viel mit Referenzen in anderen Sprachen (C#, Java, C++) arbeite. Die Verwendung dieser Art des Deserialisierens der Daten und das Erstellen der Objekte ermöglicht überhaupt keine Verwendung von Referenzen (ist das vielleicht in Web-Anwendungen falsch?). Und ich denke auch, dass das Erzeugen einer Menge nutzloser Objekte anstelle von einem pro Typ eine Verschwendung von Speicher- und CPU-Zeit ist.

Ziemlich lange Post, aber ich hoffe, es erklärt mein Problem gut.

Um es zusammenzufassen:Ich brauche eine Best-Practice für Daten vom Server auf einen Client über JSON, mit der JSON-String auf der Client-Seite zu senden Objekte zu erstellen.

+0

Ich habe normalerweise das C# Ansichtsmodell genau mit dem Javascript-Ansichtsmodell übereinstimmen. Ich finde, dass es einfacher zu pflegen ist, zu einer schnelleren Entwicklungszeit führt und in Zukunft, wenn ich etwas ändere, ist das keine große Sache (für mich ist das wichtiger als ein paar Bytes Netzwerkverkehr zu speichern). Wenn Sie keine Anwendung entwickeln, bei der jedes Byte zählt oder große Datenmengen übertragen werden, sollten Sie sich keine Gedanken darüber machen. Sie speichern bereits viele Bytes, indem Sie eine JSON-Zeichenfolge übergeben, anstatt beim erneuten Anzeigen einer neuen Seite einen vollständigen Seitenneuaufbau durchzuführen. –

+0

@dhsto Leider muss ich mit vielen Daten umgehen (etwa 1kk Objekte (As)). Aber wie Sie im Typoskript-Code sehen können, verwende ich die gleichen Klassenstrukturen in C# und in Typoskript. – bakunin

Antwort

3

Ich denke, dass Sie die JSON-Darstellung definieren müssen, die für Ihre Clientanwendung am sinnvollsten ist, und stellen Sie dann sicher, dass die Daten in diesem Format gesendet werden. Ich würde einen benutzerdefinierten Serializer erstellen, entweder mit dem integrierten JavaScriptConverter oder möglicherweise mit dem von Json.Net bereitgestellten. Die Verwendung des eingebauten Serialisierers scheint keine idealen Ergebnisse zu liefern.

+0

ok, meine eigene JSON-Struktur zu definieren würde auch für mich Sinn machen. Welche Option bevorzugen Sie? 2 oder 3? – bakunin

+0

Ich würde mit Option 3 gehen, da es die Round-Trips zum Server reduziert und (?) Ihren clientseitigen Code ein wenig vereinfachen könnte. –

1

Meine persönliche Meinung ist, dass der JSON sendet ist datengesteuert. In diesem Sinne sollte es unabhängig vom Client oder der von Ihnen verwendeten Datenbankstruktur sein.

Zum Beispiel, wenn Sie einige Foo 's abrufen, sollte der JSON alle relevanten Informationen enthalten, die ein Client might want to use, sonst werden Sie am Ende neu schreiben Ihre Daten zur Verfügung API für jeden potenziellen Kunden. Ich tendiere dazu, es aus der Perspektive der 3. Partei zu betrachten;

  • muss ich etwas über die Backing-Datenbank wissen: Nein.
  • möchte ich, dass die API mir viele relevante Informationen mit wenig Aufwand zur Verfügung stellt: ja.

Also um es zusammenzufassen:

[ 
    {"Id":1, 
    "Type": {"Id":1, "Name":"name1"} 
    }, 
    {"Id":2, 
    "Type": {"Id":2, "Name":"name2"} 
    }, 
    {"Id":3, 
    "Type": {"Id":1, "Name":"name1"} 
    } 
    {"Id":4, 
    "Type": {"Id":2, "Name":"name2"} 
    }, 
    {"Id":5, 
    "Type": {"Id":2, "Name":"name2"} 
    }, 
] 

in Ihrem Fall gültig zu sein scheint.

+0

Ich verwende relationale Datenbanken häufig und diese Art der Darstellung von Daten passt nicht in das Konzept der relationalen Datenbanken. Das mag der Grund sein, warum ich mich mit dieser Art der Datendarstellung nicht wohl fühle :) – bakunin

+1

@bakunin: kann ich mir vorstellen. Es ist definitiv eine andere Einstellung. Aber stellen Sie sich folgende Frage: Wenn Sie eine andere Personen-API verwenden, möchten Sie Daten wirklich relational abfragen/kombinieren? Oder möchten Sie einen schön aussehenden Container mit allen relevanten Informationen? Ich würde mich für Letzteres entscheiden, es sei denn, die Datenmenge ist natürlich außergewöhnlich groß. – Stefan

+0

@bakunin: eine Ergänzung: vielleicht ist die Type :: Id nicht relevant für den Client. Zum Beispiel, wenn Typen schreibgeschützt sind oder nicht durch diese Art von API-Aufruf bearbeitet werden. Wenn nicht, können Sie es weglassen. – Stefan

2

Eine gute Faustregel beim API-Design ist die Erstellung Ihrer json-Antwort, die auf Ihre Bedürfnisse auf der Clientseite zugeschnitten ist. Ich bin mir sicher, dass einige REST-Puristen nicht zustimmen würden, aber in realen Anwendungen ist die Reduzierung von XHR-Anforderungen und die Verarbeitung auf der Clientseite besser als die Einhaltung eines dogmatischen Ansatzes bei der Ressourcenmodellierung.

Wenn Sie zulassen, dass Ihre Datenbank/Server-seitigen Objektmodell-Abstraktionen in Ihren Client eindringen, kostet das viel Zeit und Aufwand. Wenn Sie Ihre Daten serialisieren, um sie an die Anforderungen Ihres Kunden anzupassen, können Sie diese Schnittstelle beibehalten, selbst wenn sich Ihre Back-End-Implementierung ändert.

+0

Wenn ich mit Referenzen in Typoskript arbeiten möchte, muss ich die Daten trotzdem zuordnen. Also 1 Objekt pro Typ und das As, das auf die verschiedenen Type-Objekte verweist. Aber vielleicht ist es falsch, mit Referenzen zu arbeiten? – bakunin

+0

Ihre clientseitigen Modelle müssen nicht unbedingt zu Ihren serverseitigen Modellen zugeordnet werden. Das Teilen von Modellen kann eine große Zeitersparnis bedeuten, aber manchmal macht es Sinn, sie für Leistung und Benutzerfreundlichkeit zu ändern oder zu verflachen. –

+0

@Scoot Puleo: Hmm .. Ich stimme dem zu. Ich könnte natürlich 2 Möglichkeiten bieten, um Daten zu bekommen.Eines, das alles wie in Option 1 enthält, und ein weiteres für meine Typoskript/Javascript-Implementierung, die mir hilft, die Daten Objekten zuzuordnen (wiederum um mit Referenzen zu arbeiten). – bakunin

2

Dies ist eine interessante Frage und ich glaube nicht, dass es eine richtige Antwort gibt, die zu allen möglichen Fällen passt.

Im Allgemeinen ist die dritte Option diejenige, die ich zuerst wählen würde. Es ist korrekt, da Sie nicht dieselben Daten (den Typ) duplizieren können, und es ist effizient, da alle Daten mit einer Anforderung verfügbar sind, was den Aufwand von XHR-Anforderungen an den Server reduzieren kann.

Nun können beide anderen Optionen einige Vorteile für verschiedene Szenarien haben. Wenn das Dataset beispielsweise klein ist, möchten Sie möglicherweise Ihre Datentypen nicht duplizieren.

Eines denke ich würde im Auge behalten, ist die beste Darstellung, die Ihren Front-End-Code passen. Sofern es sich nicht um eine generische API handelt, die Sie mit anderen Clients teilen möchten, empfehle ich, Back-End und Front-End gut zusammenzuarbeiten, um die bestmögliche Geschwindigkeit zu erreichen und gleichzeitig Ihren Code auf der Client-Seite einfacher zu halten.

hoffe es hilft

+0

nette Antwort - wie die anderen Antworten scheint es, als ob ich mit Option 3 gehen sollte, wenn ich es nur für eine interne API verwende. Nun, das ist tatsächlich der Fall, also denke ich, dass ich für Option 3 gehe – bakunin