Model obiektowy SharePoint - Client Object Model Udostępnij na: Facebook

Autor: Jakub Gutkowski

Opublikowano: 2011-08-16

Managed Client OM, inaczej zwany Client OM, jest nowością w SharePoint 2010. Został on stworzony przez Microsoft głównie z myślą o wykorzystaniu w aplikacjach Silverlight.

Przed Client OM dla programistów dostępne były jedynie WebServices, które były bardzo specyficzne – aby odwołać się do witryny i listy, należało dodać dwie różne usługi. Często się też zdarzało, że aby wykonać jakąś operację, należało w parametrze przekazać XML jako string. Na przykład, poniższy XML służył do dodania elementu poprzez usługę Lists.asxm:

<Method ID="1" Cmd="New">
   <Field Name="ID">New</Field>
   <Field Name="Title">My Title</Field>
</Method>

Taka forma komunikacji z SharePoint nie zachęcała programistów do tworzenia rozwiązań klienckich, dlatego Microsoft wpadł na genialny pomysł i wprowadził nową usługę, zwaną Client.svc, dla której utworzył osobny model, zachowując – jak tylko to możliwe – powiązanie nazw elementów z Server OM. Dla przykładu: SPWeb to Web, zaś SPSite to Site – większość nazw po prostu nie używa już przedrostka SP.

Uwaga. Usługa Client.svc jest traktowana jako internal, zgodnie z licencją korzystanie bezpośrednie z tego API może spowodować utratę wsparcia technicznego. Jeżeli wciąż chcemy odwoływać się poprzez usługi do SharePoint, możemy to wykonać, korzystając z usług, które istniały w poprzedniej wersji. Pełna lista i dokumentacja tych usług znajduje się tutaj: http://bit.ly/i0gwFM

Silverlgiht. Wspomniałem już, że Client Object Model powstał głównie dla Silverlight z tego powodu, że od wersji 2010 istnieje domyślnie zarówno web part hostujący aplikację SL, jak i część funkcjonalności (dodanie listy, witryny) jest zrobione za pomocą SL. Poza tym Microsoft (przynajmniej do czasu wypuszczenia SharePoint 2010) uważał, że przyszłość aplikacji biznesowych webowych leży w Silverlight. To się jednak trochę ostatnio zmieniło i mimo zapowiedzi Microsoft jakoś nie wierzę, aby zechcieli inwestować w SL tyle samo czasu i pieniędzy co do tej pory. Przynajmniej nie dla Silverlight Windows/Mac Runtime, bo na pewno dla WP7 Runtime pracują, i to całą parą.

Architektura

Ze względu na to, że zapytania do SharePoint mogą trwać odpowiednio długo, mogą także pobierać znaczące ilości danych, Microsoft stworzył tak naprawdę proxy, które zapisuje nasze akcje i następnie na komendę odwołuje się do Client.svc, aby pobrać dane lub wykonać operację.

Dopóki komenda nie zostanie wykonana, model obiektowy po stronie klienta nie będzie w stanie nam podać i zwrócić żadnych wartości. Dzięki takiemu podejściu możemy pracować z SharePoint całkowicie asynchronicznie. Jednak jeżeli zechcemy, to wciąż istnieje możliwość pracy synchronicznej.

Przykłady w dalszej części porównają dwa różne podejścia, tłumacząc niejasności, które mogłyby teraz powstać.

32 bit/64 bit

Przy rozwiązaniach z wykorzystaniem Client OM nie ma znaczenia, czy będziemy chcieli tworzyć aplikację 32- czy też 64-bitową. Microsoft udostępnia zbiór bibliotek dla dwóch typów procesorów i są one do pobrania pod tym adresem: http://bit.ly/f4ZJhM.

Nie jesteśmy więc zmuszeni do instalowania SharePoint, aby dostać się do niezbędnych bibliotek – nowy zbiór bibliotek daje nam większą swobodę tworzenia oprogramowania.

.NET Framework

Clinet Object Model działa zarówno dla .NET Framework 3.5 SP1, jak i dla .NET Framework 4.0 (dla innych wersji, niestety, nie zadziała). Zaś SilverlightClinet Object Model działa zarówno z Silverlight 3.0, jak i 4.

Microsoft.SharePoint.Client.dll i Microsoft.SharePoint.Client.Runtime.dll

Dla aplikacji Windows Forms, Console Application czy też WPF Microsoft stworzył dwie biblioteki – Client.dll zawiera model, z którego będziemy korzystać, zaś Client.Runtime.dll klasy i metody do wykonania żądania do usługi Client.svc.

Obydwie biblioteki znajdują się w katalogu 14 HIVE/ISAPI.

Microsoft.SharePoint.Client.Silverlight.dll i Microsoft.SharePoint.Client.Silverlight.Runtime.dll

Podobnie jak w przypadku aplikacji Windows Forms itp., aby tworzyć rozwiązanie w oparciu o Silverlight, należy dodać referencje do bibliotek Client.Silverlight.dll i Client.Silverlight.Runtime.dll, których przeznaczenie jest takie samo jako w przypadku Client.dll i Client.Runtime.dll. Z tą jednak różnicą, że można z nich korzystać tylko i wyłącznie po stronie Silverlight.

Obydwie biblioteki znajdują się w katalogu 14 HIVE/TEMPLATE/LAYOUTS/ClientBin. Jest to też miejsce, do którego aplikacja Silverlight powinna być deployowana zgodnie z zaleceniami Microsoft.

Programowanie pod Silverlight nie różni się zbytnio od tego pod Windows Forms, a jedyną znaczącą różnicą jest to, że wszystkie zapytania są wykonywane asynchronicznie i nie ma możliwości działania synchronicznego – ma to sens dlatego, że inaczej moglibyśmy zablokować całe UI użytkownika na czas zapytania.

Jako że SL OM nie różni się od normalnego modelu klienta, nie zostanie omówiony w przykładach. Jeżeli zechcecie wykonać ten sam kod, co w przykładach pod Silverlight, to trzeba wykorzystać metodę ExecuteQueryAsync (przykład jej znajduje się w przykładach od JavaScript). Więcej informacji na temat Silverlight Client Object Model można znaleźć tutaj: http://bit.ly/ftV45I i tutaj: http://bit.ly/hydu5S.

Windows Phone 7

Windows Phone 7 nie zawiera pełnej, czy też identycznej implementacji Silverlight jak na komputery stacjonarne, dlatego też na razie nie można tworzyć rozwiązań na WP 7 z wykorzystaniem Silverlight Client Object Model.

JavaScript Object Model

Wyróżniłem JavaScriptOM jako osobny punkt z jednego powodu – tak jak Server OM może być wykorzystywany tylko i wyłącznie po stronie serwera, tak JavaScript OM może być wykorzystywany tylko na konkretnej stronie SharePoint. Dokładnie mówiąc, JavaScript OM nie zezwoli nam na otwarcie połączenia do strony lub witryny znajdującej się pod innym bazowym URL oraz na otwarcie połączenia z zupełnie niezależnej strony – jest to ogólnie blokada przed cross-site scripting.

Jest to znaczące ograniczenie w porównaniu do Managed Client OM.

Poza tą różnicą JavaScript OM nie różni się sposobem działania od Client OM, jedynie mamy dostępne trochę inne nazwy metod, zgodnie z konwencją JS – camelCase: http://bit.ly/fsxy1p – jak i parę dodatkowych punktów, o których można przeczytać tutaj: http://bit.ly/el21Uv

Zbiór przykładów i możliwości JavaScript (nie tylko modelu obiektowego, ale także API udostępnione dla UI w SharePoint) można pobrać stąd: http://bit.ly/eR0rrt.

Diagram Object Model w SharePoint

Niezbyt lubię posługiwanie się diagramami, ale uważam, że bardzo dobrze podsumują one ten wstęp:

Jest to trochę uproszczony model bardzo znanego i popularnego obrazka: http://bit.ly/fdWV85, jednak do mnie bardziej ten przemawia, niż ten popularny. Przede wszystkim jest tu zaznaczone, na jakiej stronie działa JavaScript Object Model, co z bardziej znanej wersji trudno wywnioskować.

Dodatkowo pozbyłem się klas Proxy i typów requestów i odpowiedzi. Wiedza o tym, że istnieje klasa proxy, która robi XML Request do usługi Client.svc, a która następnie zwraca informacje w postaci JSON do modelu obiektowego, jest moim zdaniem trochę zbędna – ważne, aby pamiętać, że odpowiedź jest w postaci JSON, który następnie jest zamieniany na model obiektowy.

Przykłady

Zanim przejdziemy do przykładów, musimy sobie skonfigurować środowisko. Cały kod będziemy uruchamiać na komputerze, na którym zainstalowany jest SharePoint, dlatego zalecam, by Visual Studio 2010 też było zainstalowane na tym samym komputerze.

Konfiguracja dla przykładów Server OM

Dla Server Object Model tworzymy nowy projekt „Console Application” z ustawionym .NET Framework na 3.5 oraz z build na Any CPU. Dodajemy referencje do biblioteki SharePoint (14 HIVE/ISAPI/Microsoft.SharePoint.dll) i następnie podmieniamy klasę Program na następującą:

using System; 
using Microsoft.SharePoint;

namespace ServerObjectModel
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Program p = new Program();
            // p.NazwaMetodySom()
            Console.ReadLine();
        }

        private const string SiteUrl = "http://gutek-dev";
        private const string SomListTitle = "SomListExample";
        private const string SomListDesc = "Sever OM list creation example";
    
        // tutaj bedziemy wklejac metody
    }
}

Powinniśmy podmienić SiteUrl na URL do strony SharePoint w naszym środowisku.

Konfiguracja dla przykładów Client OM

Tworzymy nowy projekt „Console Application” – może być on dla framework 3.5 lub 4. Dodajemy referencję do Clinet.dll i Client.Runtime.dll z katalogu 14 HIVE/ISAPI i następnie podmieniamy klasę Program na:

using System;
using Microsoft.SharePoint.Client;

namespace ClientObjectModel
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Program p = new Program();
            //p.NazwaMetodyCom()
            Console.ReadLine();
        }

        private const string SiteUrl = "http://gutek-dev";
        private const string ComListTitle = "ComListExample";
        private const string ComListDesc = "Client OM list creation example";

        // tutaj bedziemy wklejac kod
    }
}

Powinniśmy podmienić SiteUrl na URL do strony SharePoint w naszym środowisku. Dużo więcej informacji na temat Client OM można znaleźć tutaj: http://bit.ly/fQ6EwE.

Konfiguracja dla przykładów JavaScript OM

Niestety, aby móc odwołać się za pomocą JavaScript, będziemy musieli stworzyć Web Part, jednak już opis – jak ogólnie tworzyć, konfigurować i deployować Web Party – jest poza tym artykułem. Poprowadzę Czytelnika przez proces tworzenia nowego Web Part, jednak będę zmuszony zakresem artykułu do robienia skrótów w tłumaczeniu, do czego służą poszczególne rzeczy, za co z góry przepraszam.

Tworzymy nowy projekt Web Part z gałęzi SharePoint | 2010 z ustawionym .NET Framework na 3.5:

Następnie w oknie, które nam się pojawiło, wybieramy naszą stronę SharePoint:

I klikamy Finish. Aby nie utrudniać sobie życia, nie zmieniamy nazwy WebPartu ani też nazwy kontrolki. Podmieniamy za to kod kontrolki ascx na następujący:

<%@ Control Language="C#" 
            AutoEventWireup="true" 
            CodeBehind="VisualWebPart1UserControl.ascx.cs" 
            Inherits="JsObjectModel.VisualWebPart1.VisualWebPart1UserControl" %>

<%@ Import Namespace="Microsoft.SharePoint" %> 
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
<%@ Register Tagprefix="SharePoint" 
             Namespace="Microsoft.SharePoint.WebControls" 
             Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
<%@ Register Tagprefix="Utilities" 
             Namespace="Microsoft.SharePoint.Utilities" 
             Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" 
             Namespace="System.Web.UI" 
             Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Register Tagprefix="WebPartPages" 
             Namespace="Microsoft.SharePoint.WebPartPages" 
             Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<style type="text/css">
  
    .btn 
    {
        width: 350px;
        font-family:tahoma;
        font-size:8pt; 
        height:2.1em; 
        padding-bottom:0.4em;
        padding-top:0.1em;
    }
  
</style>

<!-- 
    Wymagana referencja do pliku SP.js, ktory  udostepnia
    model obiektowy SharePoint dla JavaScript.
-->
<Sharepoint:Scriptlink Localizable="false" 
                       Name="SP.js" 
                       OnDemand="true" 
                       Runat="server" 
                       Language="javacript">
</Sharepoint:Scriptlink>  

<script type="text/javascript">

    var jsListName = 'JsListName';
    var jsListDesc = 'JavaScript OM list creation example';
   var spsWeb = null;
    // tutaj bedziemy wklejac nasz kod

</script>

<div>
    <h2 style="font-family: 15px Segoe UI Light">
        JavaScript OM Examples
    </h2>
    
    <!-- 

        Szablon przycisku, dla kazdej metody ktora chcemy przetestowac
        nalezy skopiowac przycisk, podmienic nazwe metody do wywolania
        mozna tez zmienic tytul.

        <input type="button" 
               class="btn" 
               onclick="javascript:methodJs();" 
               value="TITLE" />
        <br />
    -->
</div>

Uwaga na pierwszą linijkę – ważne, aby zarówno nazwa klasy, jak i przestrzeń nazw dla naszego WebPart się zgadzały.

Ogólne informacje

Każdy przykład ma końcówkę metody określającą typ:

  • Som – Server OM;
  • Com – Client OM;
  • Js – JavaScript OM/JS OM;

Wszystkie przykłady będą podawane naraz i omawiane. Szablony zawierają miejsca, gdzie taki kod wprowadzić. Przykłady Som i Com w metodzie Main mają miejsce na wywoływanie metod, zaś JavaScript zawiera przycisk, który należy kopiować.

Opisy przykładów zakładają, iż poprzedni przykład został przeanalizowany albo choćby przeczytany. Dodatkowo opisy są pogrupowane według typu kodu (Server OM, Client OM oraz JS OM), i skróty tych grup są wykorzystywane w dalszej części artykułu.

Przykład 1: Pobierz nazwę strony

// server
public void SitePortalNameSom()
{
    using(SPSite site = new SPSite(SiteUrl))
    {
        Console.WriteLine(site.PortalName);
    }
}

Większość operacji na stronie SharePoint nie jest dostępna z poziomu klienckiego modelu obiektowego. Dlatego ten przykład zawiera jedynie kod wykorzystujący serwerowy model obiektowy.

Na początku tworzymy odwołanie do strony pod konkretnym URL, a następnie wykorzystujemy własność PortalName zwracającą nazwę portalu, do której dana strona jest przypisana – to jest jakby kolejny poziom grupowania stron.

Obiekt SPSite nie ma reprezentacji graficznej i służy jedynie do grupowania witryn.

Przykład 2: Pobierz nazwę witryny

// server
public void WebTitleSom()
{
    using(SPSite site = new SPSite(SiteUrl))
    using(SPWeb web = site.OpenWeb())
    {
        Console.WriteLine(web.Title);
    }
}

// client
public void WebTitleCom()
{
    using(ClientContext ctx = new ClientContext(SiteUrl))
    {
        Web web = ctx.Web;
        ctx.Load(web);
        ctx.ExecuteQuery();

        Console.WriteLine(web.Title);
    }
} 

// javascript
function getWebTitle() {
    var context = SP.ClientContext.get_current();
    this.spsWeb = context.get_web();

    context.load(this.spsWeb, 'Title');

    context.executeQueryAsync(
        Function.createDelegate(this, this.onGetWebTitleSucceeded),
        Function.createDelegate(this, this.onGetWebTitleFailed));
}

function onGetWebTitleSucceeded(sender, args) {
    alert('Title: ' + this.spsWeb.get_title());
    this.spsWeb = null;
}

function onGetWebTitleFailed(sender, args) {
    alert('Get Web Title Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
    this.spsWeb = null;
}

W przykładzie pobrania nazwy witryny dzieje się już znacznie więcej. Po pierwsze, nazwa witryny jest dostępna dla każdego z modeli obiektowych SharePoint. Po drugie, wykorzystujemy zarówno synchroniczne, jak i asynchroniczne pobieranie nazwy witryny. Celem przykładu jest wyświetlenie nazwy witryny, którą definiujemy w trakcie tworzenia głównej witryny na Central Administration dla nowej strony:

Następnie możemy zmodyfikować ją w ustawieniach witryny (nie wchodząc w Central Administration):

Możemy też podać jej wartość w trakcie tworzenia pod witryny:

Tak jak strona SharePoint nie ma reprezentacji graficznej, tak witryna już ma. Jeżeli wprowadzimy adres, który wykorzystujemy w kodzie do przeglądarki, to zobaczymy to, co będzie witryną SharePoint:

Omówmy zatem konkretne przykłady.

Server OM

Podobnie jak w pierwszym przykładzie, tworzymy połączenie do strony SharePoint. Nasz adres URL odpowiada też głównej witrynie, która jest zwrócona za pomocą metody OpenWeb.

OpenWeb. Metoda OpenWeb ma kilka przeciążeń, jednak w większości przypadków korzysta się z wersji bezparametrowej. Czego się nie zaleca, to korzystanie z wersji przyjmującej fragment url – lepszym wyjściem jest już zastosowanie parametru ID witryny w postaci GUID. Ta zasada dotyczy prawie wszystkiego – GUID jest lepszy od wartości string (dlaczego tak jest, można przeczytać tutaj: http://bit.ly/9PVIoQ).

Następnie odpytujemy się o własność Title, którą ustaliliśmy podczas tworzenia witryny na przykład w Central Administration.

Client OM

Podczas korzystania z Client OM zawsze będziemy wykorzystywać klasę ClientContext, która służy jako gateway dostępu do SharePoint.

using.  Wszystkie przykłady, gdzie będzie wykorzystywany ClientContext, będą zamknięte w wyrażenie using z tego powodu, że ClientContext implementuje interfejs IDisposable. Mimo że ClientContext na Dispose nie wykonuje żadnej operacji, przez co wykorzystanie using nie jest wymagane, to jednak jest to zalecane (znając Microsoft – może on w przyszłości dodać kod, który będzie wykonywany podczas Dispose). Ubezpieczając się wyrażeniem using, gwarantujemy, że w przyszłości nie będziemy musieli go dodawać. Nie musicie się też martwić o to, że Microsoft może usunąć implementację IDisposable z ClientContext. Usunięcie jej byłoby breakingchange, co oznaczałoby, że kod napisany wcześniej by nie działał – a jak już wiemy na przykładzie nazewnictwa klas, Microsoft tego nie zrobi (przynajmniej przez 5–10 lat).

Posiadając obiekt context, jesteśmy w stanie zdefiniować zmienne, co do których chcielibyśmy, aby zostały załadowane danymi. Następnie określamy, co ma zostać załadowane poprzez metodę Load (w tym przypadku określamy, aby zostały załadowane wszystkie własności obiektu Web) i wykonujemy operację załadowania synchronicznie. Po wykonaniu komendy ExecuteQuery nasz obiekt Web będzie zawierał załadowane wszystkie własności, w tym nasz tytuł witryny.

Ładowanie własności. Pod ładowaniem wszystkich własności mam na myśli wszystkie publiczne własności, które nie są listami lub kolekcjami. Te wymagają dodatkowego określenia Load, co omówimy w następnych przykładach.