Czasami można spotkać się z sytuacją, gdy należy wprowadzić zmiany do własności konfiguracyjnych WebParta, który jest już wdrożony i skonfigurowany na witrynie. Może to być zmiana typu własności lub jej całkowite usunięcie. Niestety zastosowanie takich zmian najczęściej skończy się błędem podczas następnego podnoszenia WebParta i jedynym rozwiązaniem pozostanie usunięcie wadliwego WebParta i skonfigurowanie go na nowo.
Na szczęście istnieją sposoby radzenia sobie z powyższymi przypadkami.
Przykładowa aplikacja
Załóżmy, że w WebParcie znajdują się dwie problematyczne własności:
public int[] IntegerArray { get; set; } - nie chcemy dłużej stosować tablicy intów jako własności, ponieważ nie jest ona typu prostego i może powodować problemy (na przykład z SharePoint Designerem). Jednocześnie nie chcemy utracić jej konfiguracji, więc musimy przechwycić stare ustawienia i zapisać je w nowej postaci (na przykład jako string)
public string OldProperty { get; set; } - jest to stara, już nie wykorzystywana wartość, jednak nie możemy jej po prostu wyrzucić
Zmiany zaczynamy od stworzenia nowej własności, która będzie nową wersją własności IntegerArray. Będzie to własność typu string, a wartości będą przechowywane w niej jako liczby oddzielone przecinkami:
public string StringValues { get; set; }
Jednocześnie musimy obsłużyć wszystkie miejsca w kodzie, gdzie występuje stara własność i modyfikujemy w taki sposób, by obsługiwały nowy format. Jeśli kod kompiluje się prawidłowo, możemy usunąć starą wartość.
Następnie należy zaimplementować interfejs IVersioningPersonalizable, który posiada tylko jedną metodę Load(System.Collections.IDictionary unknownProperties). Jest ona wywoływana po deserializacji WebParta, a jeszcze przed funkcją OnInit(), dlatego jest do dobry moment na obsłużenie zmian. W zmiennej unknownProperties przechowywane są wszystkie nierozpoznane własności w postaci klucz (string, będący nazwą własności) i wartość (obiekt, który będzie można zrzutować na odpowiedni typ).
//iterujemy po wszystkich nierozpoznanych własnościach WebParta
foreach (var key in unknownProperties.Keys)
{
//jeśli jest to zmieniana własność
if (key.ToString().Equals("IntegerArray"))
{
//wiadomo, że nieistniejąca własność jest tablicą liczb całkowitych
int[] oldArray = unknownProperties[key] as int[];
StringBuilder builder = new StringBuilder();
//korzystając ze starych wartości tworzymy nową własność nie tracąc ustawień
foreach (var i in oldArray)
{
if (builder.Length > 0)
builder.Append(",");
builder.Append(i.ToString());
}
//przypisujemy wartość do nowej własności
StringValues = builder.ToString();
_isDirty = true;
}
if (key.ToString().Equals("OldProperty"))
{
//ignorujemy starą nieużywaną wartość
_isDirty = true;
}
}
}
Gdy zostanie wykryta sytuacja, w której należy zaktualizować własności WebParta zmienna _isDirty ustawiana jest na true. Taką sytuację możemy obsłużyć w funkcji OnInit():
if (_isDirty && null != WebPartManager)
{
SetPersonalizationDirty();
_isDirty = false;
}
Wywołując funkcję SetPersonalizationDirty() wymuszamy zapisanie ustawień WebParta.
Jak co roku organizujemy staże wakacyjne i właśnie rozpoczęliśmy rekrutację. Zapraszamy studentów 3, 4 lub 5 roku do udziału w wakacyjnym stażu informatycznym w firmie WebCon Sp. z o. o. Oferujemy:
Ciekawe tematy stażowe. Między innymi:
Implementacja aplikacji testowych wykorzystujących Ajax, jQuery, HTML 5.
Integracja obiegu dokumentów z systemami Microsoft (SOA).
Implementacja narzędzi do zarządzania wersjami baz danych i migracji danych.
Prace badawcze nad optymalizacją funkcji silnika WorkFlow.
Współpracę z kompetentnymi ludźmi w dynamicznie rozwijającej się firmie. Pracę z wykorzystaniem najnowszych technologii firmy Microsoft
Visual Studio 2010
Silverlight 4.0, WPF, WCF, WF
SQL Server 2008 R2, 2012
SharePoint Server 2010
Wynagrodzenie za pracę w trakcie stażu.
Perspektywę zatrudnienia po zakończeniu stażu.
Wymagania:
Studia na 3, 4 lub 5 roku kierunków informatycznych lub pokrewnych.
Dobra znajomość technologii .NET. Atutem jest znajomość ASP .NET, Ajax.
Znajomość bazy danych Microsoft SQL Server oraz pracy w środowiskach Windows.
Chęć uczenia się i poznawania nowych technologii.
Harmonogram i rekrutacja:
Przyjmowanie zgłoszeń trwa do 31 maja 2012.
Staż trwa od 1 lipca do 30 września 2012 roku.
W podaniu należy krótko opisać doświadczenie w podanych technologiach i wraz z załączonym CV przesłać na adres staz@webcon.pl.
Podania z załączonym CV należy przesyłać na adres staz@webcon.pl do 31 maja 2012.
Standardowym scenariuszem deploy'u webpartów jest umieszczenie plików dll zawierających kod webpartów w GACu lub binie, stworzenie manifestu i umieszczenie go w galerii składników webpart oraz dodanie odpowiedniego wpisu safecontrol do pliku web.config web aplikacji, w której chcemy skorzystać z naszego rozwiązania.
Problem pojawia się w momencie, gdy ten prosty wydawałoby się scenariusz nie zadziała.
Dosyć częstym komunikatem, który się pojawiaja gdy coś poszło nie tak jest:
Błąd składnika Web Part: Nie można wyświetlić lub zaimportować składnika Web Part lub formantu formularza sieci Web na tej stronie. Nie można znaleźć typu lub nie został on zarejestrowany jako bezpieczny.
Pokaż szczegóły błędu
Ukryj szczegóły dotyczące błędu
[UnsafeControlException: Nie można wyświetlić lub zaimportować składnika Web Part lub formantu formularza sieci Web na tej stronie. Nie można znaleźć typu lub nie został on zarejestrowany jako bezpieczny.]
at Microsoft.SharePoint.ApplicationRuntime.SafeControls.GetTypeFromGuid(Guid guid)
at Microsoft.SharePoint.WebPartPages.SPWebPartManager.CreateWebPartsFromRowSetData(Boolean onlyInitializeClosedWebParts)
Ten błąd informuje nas w zasadzie tylko o tym, że coś poszło nie tak i sami musimy stwierdzić co.
Jedną z możliwości jest, że nie został dodany wpis safecontrol do pliku web.config. Możemy to zweryfikować przeglądając plik web.config w katalogu głównym web aplikacji, w której chcemy skorzystać ze składnika webpart. Standardowym katalogiem dla aplikacji działających na porcie 80-tym jest katalog C:\inetpub\wwwroot\wss\VirtualDirectories\80.
Jeśli wpis safecontrol jest poprawny drugą możliwością jest niezaładowanie się pliku dll. Najlepiej wtedy odnaleźć manifest wadliwego składnika webpart w galerii składników webpart i zobaczyć, czy w GACu lub binie znajduje się plik dll, który jest wymieniony w manifeście. Jeśli go brakuje musimy zwerfyikować dlaczego go tam nie. W przeciwnym przypadku możemy dokonać recyclingu puli aplikacji lub reset serwera IIS. Gdy to się nie powiedzie możemy skorzystać z narzędzia Fuslog Viewer. Więcej informacji o tym narzędziu znajduje się tutaj
Co jednak w przypadku, gdy webpart wyrzuca nieobsłużony wyjątek i cała strona przestaje działać? Z pomocą przychodzi nam Webpart Maintenance Page. Jest to bardzo przydatna sztuczka, o której wielu ludzi zapomina. Do adresu strony, która przestała działać, można dodać parametr ?contents=1 i wtedy pojawi się strona, na której możemy zarządzać składnikami webpart. Ukrywając po kolei składniki webpart możemy zdiagnozować, który z nich powoduje błąd.
W czasie pisania aplikacji po Sharepointa bardzo często zachodzi potrzeba odwołania się do obiektów SPWeb i SPSite.Niestety obiekty te, pomimo że wydają się obiektami zarządzanymi, w rzeczywistości takie nie są. Zawierają one bowiem referencję do obiektu SPRequest, który z kolei zawiera referencję do obiektów COM odpowiedzialnych za komunikację z bazą danych. Pamięć zaalokowana przez tego typu obiekty nie zawsze jest zwalniana automatycznie, dlatego bezpośrednio po ich użyciu musimy sami zatroszczyć się o jej zwolnienie. Jeśli o to nie zadbamy możemy mieć do czynienia z tzw. wyciekiem pamięci, spadkiem wydajności naszej aplikacji, a w pewnych sytuacjach nawet z przepełnieniem stosu i zatrzymaniem się Sharepointa.
Obydwa obiekty: SPWeb i SPSite implementują interfejs IDisposable, aby je zwolnić po zakończeniu pracy z nimi wywołujemy więc na nich metodę Dispose. Możemy także skorzystać z wyrażenia using, które wywołuje Dispose automatycznie:
using (SPSite tmpSite = newSPSite("http://sharepoint"))
{
using (SPWeb tmpWeb = tmpSite.OpenWeb())
{
return tmpWeb.Name;
}
}
Kod ten jest równoważny poniższemu:
SPSite tmpSite = null;
SPWeb tmpWeb = null;
try
{
tmpSite = newSPSite("http://sharepoint");
tmpWeb = tmpSite.OpenWeb();
return tmpWeb.Name;
}
catch(Exception e)
{
//Jeżeli nie implementujemy obsługi błędów
// można użyć prostszej składni z „using”
}
finally
{
if (tmpSite!= null)
tmpSite.Dispose();
if (tmpWeb!= null)
tmpWeb.Dispose();
}
Kod z pierwszego przykładu można jeszcze uprościć:
using (SPSite tmpSite = newSPSite("http://sharepoint"))
using (SPWeb tmpWeb = tmpSite.OpenWeb())
{
return tmpWeb.Name;
}
Z upraszczaniem nie należy jednak przesadzać:
using (SPWeb tmpWeb = newSPSite("http://sharepoint").OpenWeb())
{
return tmpWeb.Name;
}
//niestety SPSite zostanie w pamięci
Jak widać na powyższym kodzie jeżeli tworzymy nowy obiekt, należy pamiętać o jego zwolnieniu. Odnosi się to również do iterowania po liście webów, gdzie podczas odwołania do elementów kolekcji są tworzone ich instancje.
using (SPSite tmpSite = newSPSite("http://sharepoint"))
{
foreach (SPWebtmpWeb intmpSite.AllWebs)
{
//tmpWeb nie zostanie zwolniony
}
}
W powyższym kodzie zwolniliśmy obiekt tmpSite, niestety każdy z webów z SPWebCollecion został w pamięci. Poprawnie powinno być tak:
using (SPSite tmpSite = newSPSite("http://sharepoint"))
{
foreach (SPWebtmpWeb intmpSite.AllWebs)
{
try
{
//jeśli chcemy można jeszcze dodać obsługę błędów
}
finally
{
if(tmpWeb!= null)
innerWeb.Dispose();
}
}
}
Zanim rozpędzimy się ze zwalnianiem jeszcze przykład do rozważenia:
using (SPSite tmpSite = SPContext.Current.Site){}
//using wywołujeDispose automatycznie, w tym wypadku raczej tego nie chcemy wykonać
Pamiętajmy:
Jeśli tworzymy nowe obiekty (za pomocą konstruktora lub funkcji (np. OpenWeb()), lub iterujemy po kolekcji należy pamiętać o zwolnieniu obiektów.
Jeśli obiekty pobierane są z kontekstu to ze zwalnianiem trzeba uważać (i pamiętać że using wywołuje Dispose automatycznie po wyjściu z sekcji kodu!).
Jeżeli używamy obiektów implementujących IDosposable należy unikać tworzenia ich w jednym wątku, a zwalniania w innym. Dlatego obiekty typu SPWeb i SPSite nie powinny być przekazywane między wątkami, ani też deklarowane jako statyczne.
O ile odwołanie do kolekcji typu SPWebCollection i SPSiteCollection nie powoduje konieczności zwalniania obiektów (kolekcja nie używa kodu niezarządzanego, tylko jej elementy), to jednak odwołania do elementów kolekcji (za pomocą [ ]), oraz dodawanie do niej elementów tworzy ich instancje które należy zwolnić.
Mając do czynienia z obiektami Sharepointa impelemtującymi interfejs IDisposable należy pamiętać że może zajść konieczność ich zwolnienia. Jak zwykle zdarzają się jednak wyjątki, np. obiektów SPSite.RootWeb,SPWeb.ParentWeb nie musimy zwalniać ręcznie.