Entwicklung

Bessere OData Calls schreiben

OData und die 7 Paramater

Das Schreiben von OData Calls zum Anlegen, Lesen, Aktualisieren oder Löschen von Datensätzen in der Fiori Entwicklung eine ganz häufige und somit gewöhnliche Tätigkeit. Leider scheint mir die Implementierung dieser als Calls häufig sehr komplex, unleserlich und fehleranfällig zu sein. Hier ein typisches Beispiel, wie ich es oft gesehen und auch selber geschrieben habe, für die Implementierung eines OData-Aufrufes zum Erstellen eines Datensatzes.

createSalesOrder: function(oSOJson) {
  var that = this;

  this.oSOCartModel.create("/SalesOrders", oSOJson, null,
    function(oData, response) {
      // Lines of random code ...
      // Lines of random code ...
      // Lines of random code ...
      // Lines of random code ...
    },
    function (oError) {
      var errorTitle = that.oResBundle.getText("ERROR");
      // Lines of random code ...
      // Lines of random code ...
      // Lines of random code ...
    },
    true, 
    "CustomURLParams='X'"
  );
}

In der createSalesOrder() wird ein Datensatz für eine neue Bestellung übergeben. Anschließend wird die create() Methode vom OData Model aufgerufen. Hier werden geschlagene sieben Parameter übergeben:

  1. Der Pfad im Model
  2. Der Datensatz an sich
  3. Dieser Parameter dient dem Setzen ob der Pad aus #1 relativ zum Kontext sein soll. Wird nicht benötigt und mittels “null” übersprungen.
  4. Eine anonyme function() für die Abwicklung eines erfolgreichen Aufrufes
  5. Eine anonyme function() für die Abwicklung eines fehlgeschlagenen Aufrufes
  6. Boolean ob der Aufruf asynchron erfolgen soll. “true” ist hier schon der Standard, wir setzen ihn einfach erneut um an den Parameter #7 gelangen zu können…
  7. Eigene URL-Parameter, die wir an das Gateway übergeben möchten.

Ziemlich viel Aufwand, oder? Richtig ärgerlich finde ich dabei, dass die Parameter #3 und 6 eigentlich nur als Platzhalter dienen müssen, um die erwartete Reihenfolge der übergebenen Werte einhalten zu können. Die anonymen function() erhöhen deutlich die Zeilen Code und häufig mehr noch, wie in meinem reduzierten Beispiel oben angedeutet. Ich muss mit der ‘that’ Variable sicherstellen, dass die Selbstreferenzen wie erwartet auf den Controller an sich zeigen und nicht im Scope der anonymen function() überschrieben wird. Die komplette createSalesOrder() ist unclean, fummelig zu schreiben mit all den Klammern und Kommas sowie schlecht zu testen. Leider finden sich diverse offizielle Tutorials und viele weitere Beispiele im SDN, oder auf StackOverflow, die genau so arbeiten. Daher würden es viele Fiori-Entwickler einfach übernommen haben. Ich habe es genau so gelernt und versuche damit zu leben. Aber muss das so sein?

FIFA FIORI 16 – Legacy Edition ?

Was mich ja schon immer stutzig gemacht hatte, war die Darstellung der create() in der offiziellen Dokumentation in der APIs der ODataModel Klasse.

OData create

Dieses “mParameters” … Ist damit eine Map gemeint? Falls ja, warum funktioniert denn die oben beschriebene Variante mit all den Paramatern? Die Antwort habe ich im Quellcode des SAPUI5-Frameworks gefunden:

	
ODataModel.prototype.create = function(sPath, oData, mParameters) {
  var oRequest, oBatchRequest, sUrl, oRequestHandle, oEntityMetadata,
      oContext, fnSuccess, fnError, bAsync = false, mUrlParams;

  if (mParameters && typeof (mParameters) == "object" && !(mParameters instanceof sap.ui.model.Context)) {
    // The object parameter syntax has been used.
    oContext	= mParameters.context;
    fnSuccess	= mParameters.success;
    mUrlParams	= mParameters.urlParameters;
    fnError	= mParameters.error;
    bAsync	= mParameters.async === true;
  } else {
    // Legacy parameter syntax is used
    oContext	= mParameters;
    fnSuccess	= arguments[3];
    fnError	= arguments[4];
}

Quelle

So! Anscheinend ist der bislang gelernte Weg des OData Calls veraltet, also “legacy”, und wird wohl für Abwärtskompatibilität noch im Framework gehalten. Nicht ungewöhnlich für SAPUI5 war das mal der Weg, nicht jedes Tutorial wurde aktualisiert und so blieb es im Kopf. Aber okay, wir können die Parameter der create() (und auch der anderen wichtigen OData-Methoden) gezielt mit einer Map ansteuern.

Map it out !

Mit dieser neuen Erkenntnis ließ sich die am Anfang dieses Posts gezeigte createSalesOrder() deutlich verschlanken:

createSalesOrder: function(oSOJson) {
  var mParams = {
    success: this._fnHandleODataSuccess,
    error: this._fnHandleODataFail,
    urlParameters: "CustomURLParams='X'"
  };
  
  this.oSOCartModel.create("/SalesOrders", oSOJson, mParams);
  
},

Was habe ich gemacht? Anstatt die Parameter nach und nach zu übergeben, habe ich einfach die drei wirklich benötigten (Erfolgshandling, Fehlerhandling, URL) in die Map “mParams” gegeben, und zwar anhand der in der Doku gelisteten Schlüssel. Ferner konnte ich dadurch jeweils die function() für das Handling des Ergebnisses des Calls auslagern. Dies macht die createSalesOrder() einfacher testbar und wiederverwertbar. Hilfsvariablen wie “that” sind ebenfalls nicht mehr nötig. Insgesamt war ich mit dem Ergebnis sehr zufrieden und kann jeden Entwickler nur empfehlen, ebenfalls auf Maps in OData-Calls umzusteigen.

PS: In der v2 der OData Klasse wird diese Legacy-Methodik schon nicht mehr unterstützt. Wer für sich mal feststellen musste “irgendwie geht mein OData bei v2 nicht mehr…” – das dürfte der Grund sein 😉