wtorek, 31 maja 2011

Gdy debugger to za mało … - sposoby raczenia sobie z błędami w webpartach

Programowanie
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.

środa, 13 kwietnia 2011

Staże wakacyjne 2011

WebCon Solutions


Jak co roku organizujemy w WebCon staże wakacyjne i właśnie rozpoczęliśmy rekrutację na staż wakacyjny dla informatyków.

Oferujemy:
  • Ciekawe tematy stażowe. Między innymi:
    • Implementacja aplikacji testowych z uwzględnieniem standardu 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, WWF
    • SQL Server 2008 R2
    • 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, Silverlight.
  • 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 2011.
  • Staż trwa od 1 lipca do 30 września 2011 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 2011.

wtorek, 21 grudnia 2010

Zwalnianie instancji obiektów SPWeb i SPSite

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 = new SPSite("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 = new SPSite("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 = new SPSite("http://sharepoint"))

using (SPWeb tmpWeb = tmpSite.OpenWeb())

{

return tmpWeb.Name;

}

Z upraszczaniem nie należy jednak przesadzać:

using (SPWeb tmpWeb = new SPSite("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 = new SPSite("http://sharepoint"))

{

        foreach (SPWeb tmpWeb in tmpSite.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 = new SPSite("http://sharepoint"))

{

        foreach (SPWeb tmpWeb in tmpSite.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łuje Dispose 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.

Szersze informację na temat co i kiedy należy zwalniać można znaleźć zapoznając się z artykułem: „Best Practices: Using Disposable Windows SharePoint Services Objects” dostępnym na MSDN.

wtorek, 5 października 2010

Integracja WorkFlow z systemami klasy ERP

WebCon Solutions
Czy można integrować WorkFlow z systemami ERP ? A może trzeba ? Więcej informacji znajdziesz tutaj.

wtorek, 28 września 2010

Jak ustawić zawartość kontrolki PeopleEditor przy użyciu Javascript


  Programowanie
 
Przy tworzeniu aplikacji dla SharePoint'a może zdarzyć się potrzeba ustawienia zawartości kontrolki PeopleEditor przy pomocy Javascript'u. Poniższy przykład wyjaśnia jak to zrobić.
 
<script type="text/javascript">

function SetPickerValue(pickerid, key, dispval)
{
    var xml = '<Entities Append="False" Error="" Separator=";" MaxHeight="3">';
    xml = xml + PreparePickerEntityXml(key, dispval);
    xml = xml + '</Entities>';

    EntityEditorCallback(xml, pickerid, true);
}

function PreparePickerEntityXml(key, dispval)
{
    return '<Entity Key="' + key + '" DisplayText="' + dispval + '" IsResolved="True" Description="' + key + '"><MultipleMatches /></Entity>';
}

</script>

Najlepszą metodą jest użycie funkcji EntityEditorCallback. Jest ona dostępna wraz z innymi wbudowanymi funkcjami dostarczonymi z SharePointem (zarówno w WSS 3.0, MOSS2007 jak i SP2010). Ta sama funkcja jest używana przez kontrolkę PeopleEditor'a do ustawiania swojej własnej wartości.
Najważniejsze są dwa pierwsze parametry funkcji - XML zawierający dane oraz identyfikator kontrolki (ClientID) której zawartość ma być ustawiona.
Powyższy kod prezentuje funkcję SetPickerValue zawierającą zarówno generację odpowieniego XML'a jak i wywołanie funkcji EntityEditorCallback.
 
Poniżej przykład użycia funkcji SetPickerValue:
 
SetPickerValue('ctl00_PlaceHolderMain_pplEdit', 'domain\\j.kowalski','Jan Kowalski');


Powyższe wywołanie generuje taki XML:

<Entities Append="False" Error="" Separator=";" MaxHeight="3">
  <Entity Key="domain\j.kowalski" DisplayText="Jan Kowalski" IsResolved="True" Description="domain\j.kowalski">
    <MultipleMatches />
  </Entity>
</Entities>


I przekazuje go do metody EntityEditorCallback.
Przy podawaniu nazwy konta domenowego należy zwrócić uwagę na znak \ (backslash). W języku Javascript jest to znak specjalny i nie można go używać bezpośrednio w parametrach tekstowych. Należy użyć \\ aby tekst był zinterpretowany jak znak backslash.

Aby ustawić zawartość PeopleEditora na kilka wartości na raz, trzeba zmodyfikować funkcję SetPickerValue tak, by generowała kilka węzłów <Entity /> w tworzonym XMLu. Każdy taki węzeł odpowiada jednej wartości w kontrolce.

BANNER