In diesem Beitrag möchte ich ein kurzes Update zu der bereits vorgestellten Document Search bringen. Denn auch wenn der dort vorgestellte Lösungsansatz für die meisten Fälle reicht und gute Ergebnisse liefert, so gibt es doch noch Verbesserungsmöglichkeiten. Ein Problem, welches die Nutzerfreundlichkeit merklich einschränkt, ist die synchrone Implementierung der Webservice Aufrufe.
Nachteile der Synchronene Webservice Aufrufe
Beide Nachteile werden durch die asynchronen Abfragen behoben: Dem Nutzer kann eine deutlichere Meldung wie „Lädt…“ angezeigt werden und die Performance profitiert ebenfalls.
Notwendige Änderungen
Der erste Schritt der Transformation zu einer asynchronen Implementierung ist die Umsetzung einer Ladeanzeige. Das Standard CRM Loading Gif, was sich auch im obigen Screenshot sehen lässt, findet sich unter der folgenden relativen Server URL: „/_imgs/AdvFind/progress.gif“. Mit diesem Wissen kann man nun eine Funktion schreiben welche eine Ladenachricht darstellt:
/** brief Zeigt die Ladenachricht an */ function showLoadingScreen() { var loadingHtml = "<tr><td class='LoadingRow' style='height:75px'></td></tr><tr><td class='LoadingRow'><img src='/_imgs/AdvFind/progress.gif'/></td></tr>"; loadingHtml += "<tr><td class='LoadingRow'>Lädt...</td></tr>"; $("#results").html(loadingHtml); $("#results").css("width", "100%"); }
Da der Nutzer nun darüber informiert wird dass der Browser arbeitet können die Webservice Aufrufe umgestellt werden. Auf SharePoint seite lässt sich dies relativ einfach bewerkstelligen. Die SPServices Bibliothek stellt hierfür einen einfachen Parameter „async“ bereit. Wird dieser auf true gesetzt führt die Bibliothek die Abfrage asychron aus. Der neue Aufruf sieht also wie folgt aus:
$().SPServices({ operation: "QueryEx", webURL: WEB_URL, queryXml: queryXml, async: true, completefunc: function (xData, Status) { //Verarbeiten der Daten } });
Da dieser Aufruf hier als Einstieg fungiert sind keine weiteren Änderungen nötig. Die CRM Seite verlässt sich jedoch stärker auf die Eigenschaften eines synchronen Aufrufs, sodas hier mehr Änderungen nötig sind. Zur Erinnerung: In der alten Implementierung wurden die Daten zu den Suchergebnissen in einer Schleife nach und nach abgefragt und durch die klare sequentielle Abarbeitung ist es einfach die Ergebnisse zuzuordnen.
Bei der neuen asynchronen Verarbeitung ist dies nicht mehr gegeben und auch die Reihenfolge in der die Antworten des Servers ankommen ist nicht vorhersehbar womit die Zuordnung erschwert wird. Hier muss nun mit Callback Funktionen gearbeitet welche die Daten zuordnen und weitere Daten abfragen. Wird eine solche Funktion bei der XrmServiceToolkit.Soap.Fetch Funktion des XrmServiceToolkit angegeben wird dieser Aufruf automatisch asynchron durchgeführt. Somit sieht die neue Abfrage der Dokumenten Speicherorte wie folgt aus:
/** brief Fragt die Entitätsdaten zu den gefundenen Pfaden ab * param searchResultsGroupByPath Gefundene Pfade * return Entitätsdaten */ function getEntityData() { //Alle Ergebnisse durchsuchen for (curPath in SEARCH_RESULTS_GROUP_BY_PATH) { //Pfad säubern und trennen var splittedPath = curPath.replace(WEB_URL, "").split("/"); getDocumentLocation(curPath, splittedPath, 0, null); } } /** brief Ermittelt den SharePoint Dokumenten Speicherort zu einem Teilpfad * param curPath Pfad nachdem gesucht wird * param splittedPath Teilpfad der gesucht wird * param splitIndex Index im Pfad * param oldLocation Alter SharePoint Dokumenten Speicherort des vorherigen Teilpfad * return SharePoint Dokumenten Speicherort, null wenn keiner gefunden wurde */ function getDocumentLocation(curPath, splittedPath, splitIndex, oldLocation) { if (splittedPath.length <= splitIndex) { DATA_LOADED_COUNT++; if (typeof oldLocation != "undefined" && oldLocation != null && oldLocation.attributes["regardingobjectid"] != null) { var entityData = new Object(); entityData.logicalName = oldLocation.attributes["regardingobjectid"].logicalName; entityData.id = oldLocation.attributes["regardingobjectid"].id; entityData.name = oldLocation.attributes["regardingobjectid"].name; entityData.Path = curPath; ENTITY_DATA[ENTITY_DATA.length] = entityData; } checkLoadingFinished(); return; } //Dokumenten Location ermitteln var fetchXml = "<fetch mapping='logical' version='1.0'>" + "<entity name='" + DOCUMENT_LOCATION_ENTITY + "'>" + "<attribute name='" + DOCUMENT_LOCATION_ENTITY + "id'/>" + "<attribute name='regardingobjectid'/>" + "<attribute name='regardingobjecttypecode'/>" + "<filter type='and'>" + "<condition attribute='relativeurl' operator='eq' value='" + splittedPath[splitIndex] + "' />"; if (oldLocation != null && typeof oldLocation != "undefined") { fetchXml += "<condition attribute='parentsiteorlocation' operator='eq' value='" + oldLocation.id + "' />"; } fetchXml += "</filter>" + "</entity>" + "</fetch>"; //Dokumenten Location abfragen XrmServiceToolkit.Soap.Fetch(fetchXml, function (retrievedDocumentLocation) { if (retrievedDocumentLocation.length == 0 && oldLocation == null) { DATA_LOADED_COUNT++; checkLoadingFinished(); return; } getDocumentLocation(curPath, splittedPath, splitIndex + 1, retrievedDocumentLocation[0]); }); }
Dem aufmerksamen Leser wird die Funktion „checkLoadingFinished“ aufgefallen sein die noch nicht näher erläutert wurde. Diese prüft ob alle notwendigen Daten geladen wurden und die Suchergebnisse angezeigt werden können. Der Quellcode der Funktion sieht folgendermaßen aus:
/** brief Prüft ob der Ladevorgang abgeschlossen ist */ function checkLoadingFinished() { var resultCount = 0; for (var curProp in SEARCH_RESULTS_GROUP_BY_PATH) { if (SEARCH_RESULTS_GROUP_BY_PATH.hasOwnProperty(curProp)) { ++resultCount; } } if (DATA_LOADED_COUNT >= resultCount) { //Alte Pfade zwischenspeichern for (curPath in SEARCH_RESULTS_GROUP_BY_PATH) { TOTAL_SEARCH_RESULTS_PATH[curPath] = SEARCH_RESULTS_GROUP_BY_PATH[curPath]; } if (ENTITY_DATA.length < PAGING_COUNT && RELEVANT_RESULT_COUNT >= PAGING_COUNT) { PAGING_START += PAGING_COUNT; loadNextPageData(); return; } finishQuery(); } }
Wie hier zu sehen ist, wird in dieser Funktion als erstes die Anzahl an Suchergebnissen ermittelt. Da hier ein Objekt als assoziatives Array genutzt wird kann man nicht einfache eine Property wie length nutzen sondern muss die Properties des Objektes zählen. Ist dies erfolgt kann durch den Zähler „DATA_LOADED_COUNT“ geprüft werden ob der Ladevorgang abgeschlossen ist. Nun werden innerhalb der Funktion „finishQuery“ die Daten wie gewohnt angezeigt.
Die if-Abfrage welche eine Paging Abfrage beinhaltet kann hier ignoriert werden. Darauf werde ich in einem zukünftigen Artikel eingehen.
Fazit
Durch die asychrone Verarbeitung ist die Nutzerfreundlichkeit deutlich erhöht da der Browser nicht mehr „einfriert“. Dieses hier vorgestellte Update lässt sich als Solution, wie auch beim vorhigen Artikel, auf Codeplex finden.
Kategorien: Dynamics CRM, Erklärt, SharePoint, Technical
Schlagwörter: CRM Document Search, Dynamics CRM, HowTo, SharePoint
Haben Sie Fragen zu diesem Artikel oder brauchen Sie Unterstützung?
Nehmen Sie mit uns Kontakt auf!
Wir unterstützen Sie gerne bei Ihren SharePoint-Vorhaben!