Wykorzystanie WCF w kodzie po stronie klienta w CRM

Ten, kto wywołuje web service’y Microsoft Dynamics CRM z kodu na formatkach (kodu JScript, po stronie klienta) wie, jak ciężko to osiągnąć. I nie chodzi tu oczywiście o składnię czy technologię (AJAX i tyle), ale o specyfikę tego działania w CRM. Po pierwsze trzeba powtarzać mnóstwo kodu; po drugie – prawie nie da się tego kodu debugować; po trzecie – łatwo o pomyłkę; po czwarte – czasem można nieźle natrudzić się z uwierzytelnieniem.

Wael Hamze na swoim blogu opublikował wczoraj bardzo fajny postna temat wykorzystywania usług Windows Communiation Foundation (WCF) z poziomu JScript’u w interfejsie Microsoft Dynamics CRM. Post jest tutaj: http://waelhamze.com/blog/crm/crm-wcf-integration/. Warto rzucić okiem, bo zdefiniowanie sobie kilku często używanych usług w WCF i np. możliwość zwracania JScript’owych obiektów zamiast parsowania zwróconego XML’a za każdym razem powodują, że wykorzystanie WCF wydaje się bardzo ciekawą opcją.

Parametry przekazywane do kontekstu plug-in’ów

Plug-in’y w Microsoft Dynamics CRM 4.0 działają zawsze w określonym kontekście. Ten kontekst prawie zawsze obsługuje parametry wejściowe i wyjściowe (lub opcjonalne). I ten post jest właśnie o tych parametrach :) .
Bardzo często zdarza się, że z przyzwyczajenia lub po skopiowaniu kodu z SDK ;) rozpoczynamy pracę z naszym plug-in’em od napisania (wklejenia) następującej linijki:

if(context.InputParameters.Properties.Contains("Target") &&
   context.InputParameters.Properties["Target"] is DynamicEntity)

Później np. przy plug-in’ie obsługującym usuwanie dziwimy się czemu nasz kod nie działa. A nie działa dlatego, że ta spopularyzowana w SDK i na stronach linijka nie sprawdza się zawsze. Bo nie może się sprawdzać. Bardzo często kolekcja parametrów przekazywanych do plug-in’ów w kontekście wcale nie zawiera klucza “Target”, a czasem żeby było trudniej ten “Target” wcale nie jest typu DynamicEntity. A jeszcze innym razem najważniejszy dla nas parametr jest w kolekcji OutputParameters, a nie InputParamaters :) . Od czego to zależy?

Oczywiście zależy od typu żądania (Request) przekazywanego do warstwy platformy. I tak np. CreateRequest przekazuje Target, ale AssignRequest przekazuje Target i Assignee. A dziesiątki innych requestów przekazuje jeszcze inne parametry. Żeby trochę ułatwić życie piszącym plug-in’y zebrałem najbardziej popularne wiadomości (messages) i wypisałem jakie parametry przekazują one plug-in’om. Poniższa tabela nie zawiera wszystkich żądań (Requests), ale te najbardziej popularne, których zapamiętanie nie raz ułatwi Wam życie…

Wiadomość (message) Nazwa parametru Typ parametru Kolekcja
Create id Guid OutputParameters
Update Target DynamicEntity InputParameters
Delete Target Moniker InputParameters
Retrieve ColumnSet ColumnSetBase InputParameters
Retrieve Target Moniker InputParameters
Retrieve BusinessEntity DynamicEntity OutputParameters
RetrieveMultiple Query QueryExpression InputParameters
RetrieveMultiple ReturnDynamicEntities Boolean InputParameters
RetrieveMultiple BusinessEntityCollection BusinessEntityCollection OutputParameters
Assign Assignee SecurityPrincipal InputParameters
Assign Target Moniker InputParameters

 

Wobec powyższego, wcześniej wspomniana popularna linijka dla operacji Delete wygląda tak:

if(context.InputParameters.Properties.Contains("Target") &&
   context.InputParameters.Properties["Target"] is Moniker)    // Moniker, a nie DynamicEntity!

Miłego programowania!

SDK 4.0.8 dostępne

4 marca Microsoft opublikował najnowszą wersję SDK do Microsoft Dynamics CRM 4.0. Aktualna wersja, oznaczona numerkiem 4.0.8, zawiera kilka poprawek oraz parę nowych informacji. Wśród nich znalazły się m.in. informacje o klasie IsvReadiness (jeden z helper’ów), która pozwala manipulować plikami zawierającymi dostosowania; kod pozwalający dodawać załączniki do notatek (popularne żądanie na forach); trochę nowych informacji o sposobach uwierzytelniania i mnóstwo nowego kodu.

SDK 4.0.8 jest dostępne tutaj: http://www.microsoft.com/downloads/details.aspx?familyid=82e632a7-faf9-41e0-8ec1-a2662aae9dfb&displaylang=en

Co to za klasa ten Customer?

Customer to jedna z klas dotępnych w Microsoft Dynamics CRM SDK (w assembly i poprzez web service) i jednocześnie koncepcyjny typ obiektów, które możemy w niektórych przypadkach przypisywać do rekordów. O co dokładnie chodzi?

W skrócie, Customer to typ referencyjny wskazujący obiekt typu account albo contact. Dzięki zastosowaniu takiej klasy, możliwe jest np. tworzenie szans sprzedaży i wstawienie do pola “Potencjalny Klient” obiektu typu account (konto) lub contact (kontakt). Nie trzeba mieć więc dwóch różnych pól – jednego wykorzystywanego jeśli klient byłby kontaktem, a drugiego jeśli klient byłby kontem. Jeden z najbardziej popularnych przykładów wykorzystania typu Customer to właśnie pole customerid w szansie sprzedaży (opportunity). Pole to zawiera GUID konta lub kontaktu, ale nie tylko. Jak więc dowiedzieć się z jakim obiektem mamy do czynienia? Bardzo prosto – ponieważ typ Customer dziedzidzy po CrmReference, pozwala przechowywać nie tylko GUID, ale i nazwę oraz typ rekordu. Dlatego, żeby dowiedzieć się z jakim obiektem mamy do czynienia w przypadku customerid (lub wszędzie tam, gdzie jest relacja typu Customer), musimy sprawdzić wartość właściwości type:

Customer c = naszaSzansaSprzedazy.customerid;
string typObiektu = c.type;    // tutaj jest informacje o typie (account lub contact)
string guidObiektu = c.Value;    // tutaj jest GUID wskazywanego obiektu

Aby pobrać teraz dane o obiekcie wskazywanym przez customerid wystarczy, że użyjemy np. metody Retrieve, wykorzystując znaleziony typ i GUID obiektu:

_naszCRMService.Retrieve(typObiektu, guidObiektu, new AllColumns());

Należy pamiętać, że NIE ma możliwości konwertowania obiektów typu Customer  do np. konta (tak naprawdę można to osiągnąć wykorzystując wewnętrzną klasę SDK – Moniker, ale to nie tego typu konwersja). Tzn, że nie można zrobić czegoś takiego:

account acc = (account)naszaSzansaSprzedazy.customerid;    // to nie zadziala!

Ograniczenia typu Customer:

Podstawowe ograniczenie tego typu to brak możliwości tworzenia nowych relacji wiążących Customer z innymi encjami. Tylko obecne w systemie relacje (m.in. szansa sprzedaży -> Customer, sprawa -> Customer) mogą korzystać z tego typu. Dodatkowe ograniczenie to fakt, że nie można zobaczyć tego typu np. w widokach, bo nie jest to typ encji, jak account czy contact.

Impersonacja plug-in’ów w Dynamics CRM

Konieczność impersonacji (użycia poświadczeń innego uprawnionego użytkownika) w kodzie plug-in’ów jest dość powszechna. Często bowiem zdarza się, że kod uruchamiany wewnątrz plug-in’u powinien być uruchamiany za pomocą konkretnego konta (np. dlatego, że konto to ma dostęp do zewnętrznego systemu).

Sytuacja domyślna:

Należałoby zacząć od kontekstu w jakim uruchamiane są plug-in’y. Otóż, plug-in’y zawsze domyślnie uruchamiają się w kontekście konta, na którym działa pula aplikacji serwera CRM. To znaczy, że w większości wypadków kod będzie działał wykorzystując konto “NETWORK SERVICE”, które w Dynamics CRM reprezentowane jest jako użytkownik “SYSTEM”.

Impersonacja innego użytkownika:

Aby użyć poświadczeń innego użytkownika, możemy zmienić konto, na którym zadziałają plug-in’y. Możemy to zrobić podczas rejestracji plug-in’u. Operacja polega na wybraniu w narzędziu do rejestracji plug-in’ów konta, które ma być użyte do wykonania kodu albo ustawienie tego konta jako wartości pola impersonatinguserid:

Impersonacja w plug-in'ie

Aby użyć wybranego podczas rejestracji konta w plug-in’ie musimy utworzyć proxy usługi sieciowej, wykorzystując metodę obiektu context:

 ICrmService naszWebService = (ICrmService)context.CreateCrmService(true);

Jeśli zostawimy puste pole impersonatinguserid i wywołamy powyższą metodę, użyjemy poświadczeń aktualnie zalogowanego użytkownika, a więc tego, którego działanie wywołało plug-in. Jeśli w czasie działania (runtime plug-in impersonation) chcielibyśmy zmienić poświadczenia z wybranego podczas rejestracji konta na użytkownika, który uruchomił plug-in, możemy utworzyć web service z następującym parametrem:

ICrmService naszWebService = (ICrmService)context.CreateCrmService(context.InitiatingUserId);

Błąd: “Method not found: Single Microsoft.Crm.Sdk.CrmFloat.get_Value()”

Ostatnio potrzebowałem użyć bardzo fajnego narzędzia dostępnego na CodePlex’ie, tzn. CRM 4.0 Bulk Data Export Tool (aplikacja wraz z kodem dostępna jest tu: http://www.codeplex.com/mscrmbulkdataexport, a jej krótki opis w tym poście: ). Niestety każda próba exportu danych kończy się błędem “Method not found: Single Microsoft.Crm.Sdk.CrmFloat.get_Value()”. Zainteresowałem się trochę bardziej tym błędem. W skrócie mówi on o tym, że nie znalazł metody get_Value() w klasie CrmFloat. W reflektorze widać jednak, że ta metoda jest obecna. O co więc chodzi?

Ano, o dość prostą rzecz… Sygnatura metody pokazuje, że metoda zwraca typ “Single”. A to nieprawda od niedawna :) . Bo CrmFloat zwraca w CRM 4.0 typ “Double”. I tyle :) . Dlatego błąd twierdzi, że nie ma metody get_Value(), która zwraca rezultat typu Single. I ma rację! Bo zwraca Double :).

Jak więc poradzić sobie z tym błędem w CRM 4.0 Bulk Data Export Tool’u? Wystarczy, że podmienimy programikowi używaną bibliotekę Microsoft.Crm.Sdk.dll na jej najnowszą wersję i przekompilujemy aplikację. Poniżej kroki, jak tego dokonać:

1. Ściągamy źródła CRM 4.0 Bulk Data Export Tool stąd: http://www.codeplex.com/mscrmbulkdataexport/Release/ProjectReleases.aspx?ReleaseId=12682

2. Ściągamy najnowsze CRM 4.0 SDK stąd: http://www.microsoft.com/downloads/details.aspx?FamilyID=82E632A7-FAF9-41E0-8EC1-A2662AAE9DFB&displaylang=en

2. Otwieramy projekt “CrmDataExport.sln” w Visual Studio.

3. Z referencji “wyrzucamy” Microsoft.Crm.Sdk i Microsoft.Crm.Sdk.TypeProxy:

4. Dodajemy referencje do najnowszych wersji tych bibliotek. W tym celu klikamy “Add reference” i w okienku w zakładce “Browse” idziemy do folderu z SDK. Z katalogu bin, wybieramy te dwie potrzebne biblioteki: 

5. Kompilujemy projekt (w menu “Build” -> “Build solution”

6. Uruchamiamy programik i wszystko działa! :) Można eksportować dowolne rekordy z naszego CRM’a.

SDK 4.0.7

1-ego października pojawiło się nowe SDK, w wersji 4.0.7. Product team znów uzupełnił brakujące informacje, poprawił błędy i dodał kilka nowych sekcji do SDK CRM’a. Najwięcej pracy zajęło chyba uzupełnienie sekcji o uprawnieniach potrzebnych dla każdej nowej, utworzonej przez nas roli zabezpieczeń (Security Role).

Nowe SDK jest do ściągnięcia tutaj: http://www.microsoft.com/downloads/details.aspx?familyid=82E632A7-FAF9-41E0-8EC1-A2662AAE9DFB&displaylang=en

Miłego czytania!

Nowe SDK 4.0.6 już dostępne

29 sierpnia pojawiła się nowa wersja SDK dla Dynamics CRM. Najnowsza wersja oznaczona jest numerkiem 4.0.6 i zawiera wiele zmian. Poprawione są m.in. błędy związane z użyciem DynamicEntity i referencji, o których pisałem tu: http://www.crmblog.pl/2008/08/bdy-w-sdk-405.html, a o których informowałem osoby z zespołu redagującego SDK dużo wcześniej :) .

Dodatkowe zmiany to pojawienie się ciekawej sekcji o pisaniu własnych rozszerzeń E-mail Router’a, lepszy opis funkcji globalnych dostępnych w JScript i opis jak dodawać Web References w Visual Studio 2008 ;) .

SDK jest dostępne tutaj: http://www.microsoft.com/downloads/details.aspx?FamilyID=82E632A7-FAF9-41E0-8EC1-A2662AAE9DFB&displaylang=en

Błędy w SDK 4.0.5

Aktualna wersja SDK dla Dynamics CRM to 4.0.5 z 30 maja 2008. Jest to wersja poprawiona, ale jak się okazuje – zawiera jeszcze sporo błędów :) . Mam na myśli błędy, które denerwują szczególnie, czyli fragmenty kodu, które rzekomo mają działać i dostarczają jakąś funkcjonalność, a najzwyczajniej w świecie nie działają…

Najwięcej błędów znajduje się we fragmentach kodu związanych jest z częścią systemu, która uległa zmianiom w wersji 4.0. W szczególności chodzi o obsługę DynamicEntity i różnych sposobów budowania kwerend do wywołania poszczególnych metod web service’u CrmService.asmx (np. ColumnSet, FilterExpression).

Jednym z przykładów błędnego kodu w SDK jest próba odwoływania się do właściwości obiektu klasy DynamicEntity poprzez Properties. W SDK mamy:

DynamicEntity entity = (DynamicEntity)retrieved.BusinessEntity;
string fullname;

for (int i = 0; i < entity.Properties.Length; i++)
{
if (entity.Properties[i].Name.ToLower() == “fullname”)

}

Ten kod w CRM 4.0 po prostu nie zadziała! Wynika to z faktu, że właściwość Properties jest teraz obiektem typu PropertyBag, więc np. nie ma właściwości Length, a Count. Dodatkowo nie można oczywiście odwoływać się do poszczególnych wartości w tej kolekcji poprzez indeksy liczbowe w pętli. Aby pobrać konkretne pole z encji, powinniśmy napisać:

if (entity.Properties.Contains(“fullname”))
{
fullName = entity.Properties["fullname"]; // a nie Properties[int i] !!!
}

Kolejne błędy znajdują się we fragmentach kodu opisujących np. dodawanie kolumn do zapytania, które chcemy wysłać web service’owi. SDK pokazuje w jednym z przykładów:

// Create a column set that holds the names of the columns to be retrieved
ColumnSet colsOrganization = new ColumnSet();
colsOrganization.Attributes = new string [] {“fullname”};

i znowu! Ten kod działał w CRM 3.0, ale nie zadziała w 4.0, jeśli użyjemy tak jak powinniśmy referencji do Microsoft.Crm.Sdk.dll :) Chyba, że “namieszamy” sobie w kodzie i dodamy “web reference” do web service’u oprócz referencji do wspomnianej DLL (czyniąc kod zupełnie nie-generycznym). Nie zadziała z tej prostej przyczyny, że w CRM 4.0 właściwość Attributes jest tylko do odczytu i zwraca listę ArrayList.
Prawidłowy kod dla CRM 4.0 to:

ColumnSet colsOrganization = new ColumnSet();
colsOrganization.AddColumn(“fullname”);



Niestety takich “nieścisłości” w SDK jest całkiem sporo… Jedyny sposób sprawdzenia, czy fragmenty kodu działają to ich uruchomienie. Nawet doświadczenie z wcześniejszymi wersjami CRM tu nie pomagają, a nawet chyba trochę szkodzą :) .

Follow

Otrzymuj każdy nowy wpis na swoją skrzynkę e-mail.