Introduzione
Lunedì pomeriggio ho scritto un post in cui facevo vedere come usare la chiamata al metodo GetListItems del web-service /_vti_bin/lists.asmx di Sharepoint per ottenere l’elenco dei files pubblicati in una document library. Lo scopo era quello di reperire l’ID univoco dell’ultimo file uploadato – file di cui vogliamo aggiornare i metadati. Il mio ragionamento era il seguente:
- ottengo l’elenco completo di tutti i files pubblicati
- faccio il parsing dell’XML restituito dal web-service
- ciclo su tutti i files restituiti, fino a quando non trovo quello che mi interessa.
Controindicazioni
Questo approccio – fondamentalmente corretto – presenta però qualche problema, come giustamente ha osservato mio fratello nel commento che mi ha lasciato nel suddetto post. Per prima cosa, genero più traffico http del dovuto: io ho bisogno di sapere solo l’ID di un solo file, mentre così facendo l’XML contiene tutto l’immaginabile relativo ad una document library.
CAML: cosa diavolo è?
Con la sua osservazione, Omar mi ha fatto conoscere un nuovo acronimo: CAML. L’acronimo sta per Collaborative Application Markup Language, e – come dice il nome – è un XML particolare da “dare in pasto” al web-service. Tramite CAML possiamo filtrare ed ordinare sui campi di una document library. Va da sè quindi che possiamo ottimizzare molto la ricerca che facciamo, dicendo per esempio di escludere le directory e di ordinare in base al campo ID in modo decrescente. L’ultimo file uploadato otterrà sempre l’ID più alto, e quindi…
Ok. Dopo averci litigato un po’, ieri sera ho raggiunto la soluzione. Il codice CAML che dobbiamo scrivere per trovare l’ID del file è la seguente:
<OrderBy>
<FieldRef Name="ID" Ascending="False" />
</OrderBy>
<Where>
<Neq>
<FieldRef Name="ContentType" />
<Value Type="Text">Folder</Value>
</Neq>
</Where>
Il blocco <OrderBy></OrderBy> esprime ovviamente l’ordinamento, che avviene per ID in modo decrescente (grande –> piccolo). Il blocco <Where></Where> esprime eventuali filtri (inclusivi od esclusivi) da applicare alla ricerca. In questo caso, diciamo che il campo ContentType NON deve essere uguale (operatore Neq) a Folder.
Non di solo CAML vivrà la mia applicazione!
CAML deve essere eseguito sul server WSS per fornire un risultato. Io lo eseguo con C#, voi fate un po’ come volete.
Ho riscritto praticamente da zero il metodo ReadFileID che avevo pubblicato lunedì. La versione attuale è la seguente:
1 public void ReadFileID(string listName, string fileName)
2 {
3 Lists l = new Lists();
4 l.Credentials = new NetworkCredential(this.Username, this.Password, this.Domain);
5
6 XmlDocument doc = new XmlDocument();
7 XmlNode query = doc.CreateNode(XmlNodeType.Element, "Query", string.Empty);
8
9 string xml = ResourceHelper.GetResourceContent("UploaderMOSS.XML.ListQuery.xml");
10
11 XmlNode node = l.GetListItems(listName, null, query, null, "1", null, null);
12
13 node = node.FirstChild.NextSibling.FirstChild.NextSibling;
14
15 this.CacheFilesID.Add(fileName, node.Attributes["ows_ID"].Value);
16 }
Riga (3) e (4): istanzio il web-service con le credenziali corrette.
Riga (6) e (7): creo un XmlDocument e creo il nodo <Query></Query>. All’interno di questi tag viene messo il blocco CAML visto sopra, riga (9). Ecco perchè non ho messo <Query></Query> direttamente nel CAML: preferisco farlo via C# ed innestare il CAML nel nodo creato a riga (7).
Riga (11): lancio la chiamata al metodo GetListItems. I parametri sono: il nome della lista (in chiaro), il nome della view (opzionale, in chiaro), l’oggetto query, un blocco XAML per i fields da vedere, il row-count, un blocco XAML per le query options, ed il WebID (opzionale?). Notare che – al contrario di quanto facevo con la vecchia versione – qui il row-count è messo a “1”.
Il resto non è nulla di che: navigo nell’XMLNode restituito dal web-service ed aggiungo alla cache privata della classe l’ID che abbiamo chiesto. Il metodo potrebbe aggiungerlo alla cache e restituirlo al chiamante, che forse sarebbe più comodo. Sono scelte: io l’ho fatto così.
Eventuali problemi
Nei giorni scorsi io ho avuto parecchi problemi ad utilizzare questa tecnica. Secondo me, dovete tener ben presente le seguenti cose:
- occhio a come create il CAML. Se non state attenti, potrà capitarvi di annidare un nodo <Query> dentro un nodo <Query>, e questo ovviamente al run-time di WSS non piace molto. Lo stesso vale per tutto il resto della query.
- il campo ID deve essere numerico, altrimenti – lo sapete meglio di me – l’ordinamento va a pallino. Sembra uno scherzo, ma io ho una document library con un campo ID che non viene ordinato nel modo corretto. Deduco che non sia numerico, ma non riesco a capirlo. Se non ci credete, eccola qua…