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.

BANNER