Początki często bywają trudne. Początek nauki nowej technologii także bywa trudny i nie jest wolny od błędów. Na błędach można się sporo nauczyć, nie tylko na swoich. W dzisiejszym artykule przestawię kilka porad, co robić, aby uniknąć pomyłek, które mogą pojawić się podczas programowania aplikacji na platformę SharePoint, szczególnie na początku tej pasjonującej przygody.
Nazewnictwo
SharePoint ma szereg wbudowanych kolumn i typów zawartości, z pomocą których użytkownik może budować własne listy (ang. Custom Lists). Jednak zazwyczaj, szczególnie w dużych projektach, tworzy się własne typy i kolumny (ang. Custom Content Types, Custom Columns).
Tworząc nowy typ zawartości (ang. Content Type) musimy mu przypisać unikatowe ID oraz nazwę (ang. Name). Sama idea jest bardzo prosta, jednak stwarza sytuację, w której bardzo łatwo popełnić błąd. Istnieje bowiem możliwość, że nadamy nowemu typowi nazwę, która istnieje już wśród wbudowanych typów zawartości. Podczas próby instalacji takiego rozwiązania w środowisku SharePointa, dostaniemy błąd. Należy zatem zadbać, aby nazwa w tworzonym typie zawartości była unikatowa. Warto sprawdzić listę istniejących typów zawartości za pomocą interpretera poleceń PowerShell, wpisując np. następujące komendy:
$web = Get-SPWeb -Identity http://[tutaj podajemy URL naszej Site Collection, bez użycia cudzysłowu]
$contentTypes = $web.ContentTypes
$web.Dispose()
$contentTypes | Select Name | Sort Name
Podobnie jest z tworzeniem nowej kolumny. Dodając nową kolumnę, uzupełniamy jej nazwę wyświetlaną (ang. Title) i nazwę wewnętrzną (ang. Internal Name). Nazwa wewnętrzna musi być unikatowa.
Poniżej znajduje się przykład błędu, jaki pojawił się przy próbie instalacji rozwiązania zawierającego kod tworzący nową kolumnę z istniejącą już nazwą wewnętrzną – EmailBody. Można zauważyć, że w treści błędu nie pojawia się ta nazwa, której użycie powodowało błąd. Treść mówi natomiast, że bezpośrednią przyczyną błędu była próba zmiany atrybutu Hidden istniejącej już kolumny – nowa kolumna miała domyślnie ustawiony atrybut Hidden na ‘false’, a przez ustawienie InternalName na istniejącą już nazwę EmailBody, zostało to potraktowane jako próba modyfikacji atrybutu istniejącej już ukrytej kolumny EmailBody.
Jeśli chodzi o nazwę wyświetlaną, to w przypadku kolumn nazwa ta nie musi być unikatowa, ale lepiej nie powielać istniejących już nazw, jeśli nie ma takiej potrzeby. Zdarza się, że użycie istniejącej już nazwy wyświetlanej dla nowej kolumny powoduje nie do końca poprawne działanie aplikacji. Wprawdzie nie dostaniemy jawnie żadnego błędu, ale możemy nie osiągnąć zamierzonego efektu. Warto więc zapoznać się z nazwami istniejących już kolumn, żeby uniknąć błędu podczas instalacji rozwiązania.
Nazwy istniejących już kolumn można sprawdzić dwiema metodami. Jeśli interesuje nas sprawdzenie nazwy konkretnego pola, można sprawdzić jego nazwę wewnętrzną w Centralnej Administracji platformy SharePoint: Site Settings -> Web Designer Galleries -> Site Columns.
Poniżej znajduje się przykład, na którym zaznaczone zostały: nazwa wewnętrzna (w adresie URL: field=WorkAddress) oraz nazwa wyświetlana (Column name: Address).
Jednak lepszym sposobem jest użycie do tego celu PowerShella, ponieważ za jego pomocą można otrzymać posortowaną listę składającą się z nazw wyświetlanych wszystkich kolumn istniejących w obrębie danej kolekcji stron (ang. Site Collection). Wystarczy użyć następujących przykładowych komend:
$web = Get-SPWeb -Identity http://[tutaj podajemy URL naszej Site Collection, bez użycia cudzysłowu]
$siteFields = $web.Fields
$web.Dispose()
$siteFields | Select InternalName | Sort InternalName
W przypadku, gdy chcemy sprawdzić także nazwy wyświetlane danych kolumn, w komendach należy użyć Title zamiast InternalName. Można też dodać Title do polecenia Select, wtedy dostaniemy listę kolumn z ich nazwami wyświetlanymi oraz wewnętrznymi.
Kiedy potrzebujemy za pomocą naszego kodu przeczytać lub ustawić wartości danej kolumny, musimy się odpowiednio do tej kolumny odwołać. Najlepiej odwołać się do nazwy wewnętrznej, ponieważ ta nazwa jest niezmienna. Nazwę wyświetlaną można łatwo zmienić w interfejsie użytkownika, natomiast nazwa wewnętrzna jest nadawana podczas tworzenia kolumny i nie można jej zmienić z poziomu interfejsu użytkownika. Dlatego właśnie najbezpieczniej jest używać nazwy wewnętrznej podczas pracy z wartościami kolumny.
Kolejną kwestią, o której warto wspomnieć, jest długość nazwy wewnętrznej. Dopuszczalna liczba znaków takiej nazwy dla kolumny dodawanej do listy SharePointowej to 32. Próba dodania do listy kolumny o dłuższej nazwie zakończy się ucięciem nadmiarowych znaków dla zapisu nazwy wewnętrznej. Jeśli chodzi o limit nazwy wewnętrznej dla kolumny dodawanej do biblioteki SharePointowej, to mamy do dyspozycji 256 znaków.
Poniżej przedstawione zostaną przykłady nazw (wyświetlanej oraz wewnętrznej) dla listy
i biblioteki.
Kolejnym zagadnieniem w ramach niniejszej sekcji jest nazewnictwo list. Generalnie lista nie ma nazwy wewnętrznej w takim rozumieniu, jak w przypadku kolumny. Jednak mechanizm tworzenia instancji listy w SharePoincie pozwala na nadanie jej takiej nazwy, która będzie niezmienna. Wraz z listą tworzony jest bowiem folder główny (ang. Root Folder), którego nazwa (ang. Root Folder Name) jest stała i znajduje się w URL tej listy. Nazwa folderu głównego listy powinna być krótka, a zarazem dobrze opisująca przeznaczenie danej listy. Podobnie jak w przypadku wewnętrznych nazw dla kolumn, nie powinniśmy używać ani spacji, ani znaków szczególnych, tworząc nazwę folderu głównego listy.Podczas ustalania nazwy wewnętrznej dla tworzonej kolumny, należy także zwrócić uwagę na spację oraz znaki specjalne. Dobrą praktyką jest używanie ciągów liter bez spacji czy znaków specjalnych, ponieważ każda użyta spacja (lub znak specjalny) zostanie automatycznie zakodowana odpowiednim kodem. Cyfry mogą być używane w nazwie wewnętrznej, aczkolwiek lepiej trzymać się nazw składających się z samych liter. Poniżej znajduje się przykład, w jaki sposób przypisana do kolumny nazwa została zmodyfikowana do postaci nazwy wewnętrznej:
Oprócz nazwy folderu głównego, lista ma także swój tytuł (ang. Title), który jest nazwą wyświetlaną dla listy, czyli jest widoczny przy każdym wystąpieniu listy na poziomie kolekcji stron. Tytuł powinien być bardziej czytelną i elegancką wersją nazwy folderu głównego listy. Na przykład dla nazwy folderu głównego „WorkflowHistory” odpowiednim tytułem listy będzie „Workflow History”.
Dostarczanie funkcjonalności
Dostarczanie funkcjonalności (ang. Provisioning) to proces tworzenia poszczególnych elementów aplikacji na platformie SharePoint. Zawiera się w nim tworzenie typów zawartości, kolumn, list, bibliotek, dodawanie ich do poszczególnych logicznych części funkcjonalności (ang. Features), a następnie, za pomocą aktywowania odpowiednich funkcji, dodawanie tych elementów do wybranej kolekcji stron. Częstą przyczyną początkowo niezrozumiałych błędów z dostarczaniem funkcjonalności jest niewłaściwa praca z polami typu Lookup. Pole typu Lookup to taka kolumna na liście, której wartość odnosi się do innej listy. Przykład: mając na liście A kolumnę, która jest odniesieniem (ang. Lookup Field) do listy B, automatycznie dostajemy dostęp do tych właściwości danego obiektu z listy B, które są wspierane przez owo odniesienie. Mówiąc inaczej, pole typu Lookup zapewnia nam dostęp do określonych właściwości obiektu znajdującego się na liście B z poziomu listy A. Jednak zawsze przed dodaniem kolumny do listy musimy taką kolumnę stworzyć. Kolumnę typu Lookup tworzy się nieco inaczej niż pozostałe kolumny. Do jej stworzenia potrzebna jest lista źródłowa, na którą wskazuje dane odniesienie.
Z poziomu kodu, tworzenie pola polega po prostu na stworzeniu nowego obiektu i uzupełnieniu jego właściwości odpowiednimi wartościami. W przypadku pola typu Lookup oprócz Title i InternalName musimy także wskazać wspomnianą wcześniej listę źródłową. Uzupełniamy także LookupField, wybierając w ten sposób pole na liście źródłowej, na którego wartość będzie wskazywała kolumna typu Lookup.
Dlatego właśnie ważne jest, aby kod tworzący listę, do której ma się odwoływać dane pole typu Lookup, był wykonywany przed kodem tworzącym samą kolumnę. Jednak jeśli w trakcie wykonywania kodu tworzącego pole typu Lookup okaże się, że odwołujemy się do listy źródłowej, która nie istnieje (np. przez podanie niewłaściwej nazwy listy), otrzymamy błąd:
Error Message: The lookup field refers to a list that cannot be found.
Natomiast usunięcie listy źródłowej w trakcie działania aplikacji bez usunięcia pola typu Lookup spowoduje brak możliwości określenia wartości pola typu Lookup dla nowo tworzonych elementów (ang. Items) na liście. Dla elementów stworzonych wcześniej, wartości kolumny odnoszącej się do listy źródłowej będą nadal widoczne, ale nie będą już wartościami typu Lookup (mogą być traktowane np. jako pole tekstowe).
Używanie kolumn typu Lookup niesie za sobą także ograniczenia ilościowe. Na widoku listy może znajdować się maksymalnie 12 pól typu Lookup (dotyczy to także pól typu Managed Metadata czy People Group). Więc jeśli mamy rozbudowany obiekt (w którego skład wchodzi więcej niż 12 pól typu Lookup) i wszystkie jego pola chcemy zawrzeć w widoku listy, spotkamy się z błędem:
Error loading content. Value does not fall within the expected range. This error could be caused by having more lookup columns in the list than the ‘Lookup View Threshold Setting’ in Central Administration.
Aby tego uniknąć należy uwzględniać powyższe ograniczenie w trakcie tworzenia klas obiektów, które będą wyświetlanie na widoku listy. Teoretycznie, w opcjach można zwiększyć dopuszczalną na widoku liczbę pól typu Lookup, jednakże nie jest to zalecane, ponieważ takie działania mogą znacznie zmniejszyć wydajność aplikacji.
Mapowanie
Jeśli w naszej aplikacji SharePointowej używamy Web Services, dobrą praktyką jest stworzenie modelu dla Obiektów Transferu Danych (ang. Data Transfer Object – DTO). Jedynym zadaniem modelu DTO jest przekazywanie obiektów pomiędzy warstwami aplikacji (pomiędzy modelem domenowym a serwisem). Nie znajdziemy tu żadnej logiki biznesowej. Do poprawnego działania Web Services aplikacji potrzebne jest dokładnie odzwierciedlenie modelu domenowego na DTO. Konwersji można dokonać ręcznie, ale w dużych projektach często używa się AutoMappera. Kiedy wykorzystujemy rozwiązanie automatyczne, należy pamiętać o odpowiednim przygotowaniu danych do mapowania. Po pierwsze, musimy skonfigurować mapowanie poprzez wskazanie, które klasy będą w nim uczestniczyły. Jeśli typy i właściwości klasy DTO są wiernym odzwierciedleniem klasy domenowej (nazwy są identyczne, a typy się pokrywają), wtedy wystarczy stworzenie mapy. W przypadku, kiedy mamy do czynienia z tzw. spłaszczeniem obiektów (ang. Flattening) – czyli z upraszczaniem rozbudowanych obiektów do prostego modelu DTO z zachowaniem odpowiedniej konwencji dotyczącej nazewnictwa (np. jeśli mamy klasę Book, która zawiera właściwość noszącą nazwę Name, w klasie BookDTO można stworzyć właściwość o nazwie BookName), wtedy także wystarczy samo stworzenie mapy bez użycia metody ForMember.
Jednak, jeśli nazwy odpowiadających sobie właściwości różnią się lub nie jest możliwa automatyczna konwersja typów, należy wyszczególnić, w jaki sposób chcemy dokonać mapowania poprzez użycie metody ForMember. Poniżej przedstawiony jest kod, zawierający tworzenie mapy
z uwzględnieniem mapowania pola, którego nazwa różni się pomiędzy klasami domenową i DTO:
private void ConfigureMappings()
{
CreateMap<Book, BookDTO>()
.ForMember(x => x.Print, opt => opt.MapFrom(y => y.PublishingHouse));
}
Jeśli wcześniej nie pracowaliśmy z AutoMapperem, mogą zdarzać się błędy wynikające
z niewłaściwego przygotowania klas do mapowania. Wtedy możemy otrzymać niewłaściwe lub niepełne dane, które zostały wystawione do WebService. Może także pojawić się błąd, gdy nie uwzględnimy mapowania właściwości, które różnią się nazwą czy typem lub gdy przeprowadzimy mapowanie w niewłaściwy sposób. Przykładowy fragment treści błędu mapowania:
{"GetBooksResult":{"ErrorMessage":"\u000aUnmapped members were found.
Review the types and members below.\u000aAdd a custom mapping expression, ignore, add a custom resolver, or modify the source\/destination
type\u000aFor no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters\u000a
Logika biznesowa
Tworząc aplikację SharePointową z poziomu kodu, potrzebujemy mechanizmu do zarządzania znajdującymi się na niej danymi. Platforma .NET udostępnia model kliencki CSOM (ang. Client-Side Object Model), dla JavaScript modelem tym jest JSOM lub/i REST. Tworząc mniej lub bardziej rozbudowaną infrastrukturę logiki biznesowej, możemy operować na danych SharePointowych, uzyskując do nich dostęp poprzez kontekst (np. ClientContext). Odwołując się do obiektów utworzonych w kolekcji stron musimy zachować odpowiedni schemat. Jest to bardzo ważne, ponieważ specyfika pracy z modelem CSOM wymaga zdefiniowania danych, które chcemy pobrać i które następnie będzie można przetwarzać. Kolejność działań przestawia się następująco: najpierw pobierany jest kontekst dla naszej strony na podstawie podanego adresu URL. Następnie, z kontekstu pobieramy Web, który jest nadrzędną stroną (ang. Root Site) w naszej kolekcji stron. Jeśli chcemy operować na listach, to je także musimy załadować. Możemy też pobrać konkretną listę po jej nazwie wyświetlanej (Title). Nazwę tę, podobnie jak w przypadku kolumn, można modyfikować z poziomu interfejsu użytkownika, zatem nie mamy pewności, czy takie odwołanie zostanie poprawnie wykonane. Drugim sposobem jest odwołanie się do listy za pomocą nazwy jej folderu głównego (kwestia tworzenia nazw dla listy zostałą poruszona na końcu sekcji „Nazewnictwo”). Nazwę folderu głównego można odczytać właśnie z adresu URL lub odwołując się do właściwości listy, a konkretnie do RootFolder.Name. Kiedy lista jest już pobrana, można wyciągnąć z niej dane. Do tego celu służy zapytanie (ang. Query). Na platformie .NET mamy dostępną bibliotekę Camlex, dzięki której można budować zapytania w prosty sposób za pomocą wyrażeń, które zostaną przekonwertowane na język zapytań właściwy dla SharePointowej bazy danych (ang. Collaborative Application Markup Language – CAML).
Podczas próby operacji na danych z listy musimy pamiętać o sprawdzeniu, czy wraz z listą dostaliśmy jakiekolwiek elementy. Przed wykonaniem operacji na obiektach zwracanych z listy, należy sprawdzić, czy mamy na czym operować, czyli czy lista nie była pusta lub czy zapytanie wykonywane na liście zwróciło jakieś elementy. W przeciwnym razie, możemy otrzymać błąd związany z próbą odwołania się do pustego obiektu.
Testy deweloperskie
Kiedy pracujemy nad aplikacją, w której dostępność funkcjonalności zależy od nadanych ról, należy testować swoje rozwiązania dla użytkowników z różnymi uprawnieniami. Częstym błędem jest sprawdzanie działania kodu tylko z poziomu administratora. Jeśli chodzi o SharePointa, to najczęściej tylko administrator (lub Site Owner) posiada dostęp do wszystkich dodawanych list, zawartości, opcji itp. Dlatego, jeśli chcemy sprawdzić, czy dana kolumna została stworzona i poprawnie dodana do odpowiedniej listy, to owszem, sprawdzimy to, logując się na konto z uprawnieniami administratora. Jednak jeśli chcemy testować zagadnienia związane z samym przepływem pracy (ang. Workflow), dobrze jest wcielić się w role użytkowników z różnymi uprawnieniami. Czasami w bazie testowej istnieją już odpowiednie konta, czasami trzeba dodać takie konto samodzielnie.
Często aplikacje SharePointowe są używane do zarządzania różnymi rodzajami formularzy. Zazwyczaj dostępne opcje zależą od poziomu uprawnień w procesie przepływu pracy. Administrator ma uprawnienia nadrzędne w stosunku do wszystkich innych funkcji. Dlatego też zawsze będzie miał dostęp do takiego formularza. Często, w zależności od tego, czy zalogowany użytkownik jest administratorem, wywoływana jest (lub nie) odpowiednia metoda do czytania elementów z bazy. Dla administratora zazwyczaj czytamy wszystkie elementy, a dla innych użytkowników elementy będą odpowiednio filtrowane. Dlatego też, testując stworzone metody tylko dla administratora, nie będziemy w stanie sprawdzić, czy stworzyliśmy odpowiednie zapytanie filtrujące wyniki.
Podsumowanie
SharePoint jest potężną, wielozadaniową platformą, która ułatwia zarządzanie przepływem danych i pracy w sieciach korporacyjnych. Dlatego też poznanie jego struktury, specyfiki, a także metod tworzenia oprogramowania w ramach tej platformy wymaga czasu i cierpliwości, a przede wszystkim praktyki. Mam nadzieję, że ten artykuł będzie wsparciem w zdobywaniu doświadczenia w tworzeniu aplikacji na platformę SharePoint.
Dzieki za rady napewno skorzystam