8

Introduction to SharePoint Development

WHAT’S IN THIS CHAPTER?

  • How to use the server-side object model
  • How to use Collaborative Application Markup Language (CAML) to provision items and retrieve information
  • How to use LINQ to SharePoint
  • What a Web Part is and how to build one
  • Understanding the REST interface
  • How to use the client-side object model
  • How to use the SharePoint web services

It is true to say that SharePoint is a product, in the sense that you can order, buy, and install it, as you would other software products. However, SharePoint is so flexible and extensible, it’s also reasonable to use talk about SharePoint as a platform or a framework. SharePoint’s breadth of functionality can be the platform upon which you build other applications, and SharePoint’s functionality can provide the supporting structure for your custom applications.

For example, imagine that you work for a company that has offices abroad. Before you can travel to another country, you must fill out an online form requesting permission to go. If your manager approves the trip, you must add an entry to the company calendar. Upon return, you must fill out an expense report, detailing expenses for the trip. Your manager must approve the expense request, at which point someone from the accounting department is notified of the expense report and reimburses you.

To build the functionality described in this scenario using SharePoint, you would probably take advantage of some of the fundamental components found in SharePoint, like InfoPath forms, workflows, and lists. However, the end result of combining these features would be the creation of a true application — that is, software that helps users execute a specific set of tasks.

Many times, the available functionality in SharePoint is not enough to achieve the kind of application you want; it may be necessary to create a custom form, define a custom workflow, or create a custom list definition. Luckily, a number of ways exist to interact with SharePoint programmatically, whether that means extending the core functionality of the product itself, or accessing and manipulating the data stored within SharePoint.

This chapter gives you an overview of how you can use the server object model and Collaborative Application Markup Language (CAML) to access and manipulate data stored in SharePoint. You learn how to simplify the process of accessing information in SharePoint using LINQ to SharePoint. This chapter introduces you to compiled and visual Web Parts, which you can use to create a custom user experience. In addition, this chapter helps you understand how you can access data stored in SharePoint from an external location using the client-side object model, SharePoint’s REST interface, and SharePoint web services.

Developers can modify and extend the SharePoint environment in myriad ways. This chapter is by no means exhaustive, but is meant to give you a brief overview of some of the kinds of SharePoint 2010 development you can do. To dive deeper into developing SharePoint 2010 solutions, consider reading Wrox’s other books, Beginning SharePoint 2010 Development and Professional SharePoint 2010 Development.

CUSTOMIZATION VS. DEVELOPMENT

In the SharePoint world, customization often refers to changes that are made to a SharePoint environment using the browser interface or a tool like SharePoint Designer. Customizations are changes that get stored in a SharePoint content database and have no relationship to a file on the filesystem. Development usually refers to the creation of solution packages and features that deploy assemblies and/or other files to the SharePoint server(s) in your farm. These files might add content to a content database, but they originate as files on the filesystem. Development is often done using a tool like Visual Studio.

AN INTRODUCTION TO THE SHAREPOINT SERVER-SIDE OBJECT MODEL

There are many times when the code you write to manipulate SharePoint will run directly on your SharePoint server. In these cases, your custom code will interact with the assemblies, which were installed as a part of the SharePoint product. In this kind of scenario, your code is interacting with the SharePoint server-side object model. This is in contrast to code you write, which could be executed on a different server or run within the context of a client application, and which will be discussed later in the chapter.

The SharePoint server-side object model is contained in several assemblies that are added to your server when SharePoint is installed. Most of these assemblies reside in the ISAPI folder in the SharePoint root. Each assembly contains one or more primary namespaces, such as Microsoft.SharePoint, Microsoft.SharePoint.PowerShell, or Microsoft.BusinessData.MetadataModel. (For a list of the namespaces used in SharePoint Foundation 2010, including a mapping of namespaces to assemblies, visit http://msdn.microsoft.com/en-us/library/ms453225.aspx.)

SHAREPOINT 2010 DEVELOPMENT AND WINDOWS 7

If you are writing code that will be compiled into an assembly, and your assembly references a SharePoint assembly, the SharePoint assembly must be installed on a functioning SharePoint server. Because SharePoint must be installed on a server running Windows Server 2008 when deployed in a production environment, developers often develop their solutions as well on a computer running Windows Server 2008 (whether the OS is installed on a physical drive or in a virtual environment). However, it is possible to install SharePoint on a development machine running Windows Vista or Windows 7 OS, provided additional changes are manually applied. With that said, Windows Vista or Windows 7 machines should never be used as production environments for SharePoint.

Luckily, the object model is fairly intuitive for people who are familiar with SharePoint. If you know what a list, list item, or folder is in SharePoint, it’s not hard to figure out what an SPList, SPListItem, or SPFolder object is. Table 8-1 gives you an overview of some of the most commonly used classes.

Table 8-1: Commonly Used Classes in the SharePoint Foundation Object Model

CLASS NAME DESCRIPTION
SPFarm The very top-level object, providing access to configuration information for the entire SharePoint farm
SPWebApplication A load-balanced IIS web application that is part of the SharePoint farm
SPSite A site collection inside a web application
SPWeb An individual site within a site collection
SPList A list within a particular site
SPListItem An item in a SharePoint list
SPFolder A folder within a SharePoint site
SPFile A document stored in a SharePoint list
SPGroup A SharePoint group
SPUser A SharePoint user
SPContentType A content type
SPField A column (either a site column or a list column, depending on its location)
SPView The view on a particular list
image

Note that an SPSite object represents a site collection, not a site.

The object model is fairly hierarchical in nature, reflecting the hierarchical nature of SharePoint itself. Just as a site contains lists, so an SPWeb object contains SPList objects. Following this convention, nearly every object is a member of a larger collection of similar objects. This facilitates the ability to iterate through multiple items at once. For instance, SPWeb.Lists returns an SPListCollection object. An SPListCollection object contains a collection of SPList objects. Figure 8-1 gives you an idea of the object hierarchy.

It’s possible to move both up and down the object hierarchy. For instance, to find the parent site to which a list belongs, you can easily access the ParentWeb property of the SPList object with which you’re working. In the same way, accessing the Site property of an SPWeb object returns the site collection to which the current site belongs. The SPSite.WebApplication property returns the web application of which the site collection is a part, and so on. Figure 8-2 shows you how you can traverse the object model up the hierarchy.

Because SharePoint utilizes a hierarchy of sites with a site collection, one of the most common things you’ll need to do is get a reference to a site collection or a particular site within the site collection as a starting point. You have two ways to access an SPSite or SPWeb object within SharePoint:

  • Using the current context: If your code is running within a SharePoint site that is being accessed via a browser, it’s possible to access the context in which the code is running using the Current property of the SPContext object. This allows your code to behave dynamically based on the location in which your code is executed. For instance, if you created a Web Part that displayed the title of the current site, you could retrieve that value by accessing SPContext.Current.Web.Title.
  • Creating a new object using a constructor: If your code is executing in a console or Windows application, a user is not browsing a SharePoint site, so there is no way to access any sort of context. Instead, it’s necessary to create a new site collection object. You can use various constructors; some allow you to pass in the URL or GUID of the site collection you want to open, and others allow you to also pass in the credentials of the account with which you want to access SharePoint. For example, if you wanted to access the title of the site at the URL http://intranet/sites/hr/health, you could do so using the following code:

using (SPSite hrSite = new SPSite("http://intranet/sites/hr"))

{

    SPWeb healthWeb = hrSite.OpenWeb("health");

    string title = healthWeb.Title;

}

OBJECT DISPOSAL

It’s often necessary to explicitly dispose of SharePoint objects like SPSite and SPWeb to prevent them from remaining active and taking up an increasing amount of server memory. It’s possible to dispose of objects either by nesting your code in a using block or by using a try/catch block and explicitly disposing of your object in a finally block.

For a complete explanation of when you should dispose of objects see the SDK article titled “Disposing Objects” at http://msdn.microsoft.com/en-us/library/ee557362.aspx. In addition, the Microsoft team has released the SharePoint Dispose Checker Tool, which you can download at http://code.msdn.microsoft.com/SPDisposeCheck.

Working with Site Collections, Webs, Lists, and List Items

One of the most common tasks you will probably encounter is to retrieve information stored in a SharePoint site collection.

Each site collection has a single top-level site (called the root site or the root web), which can be accessed by retrieving the SPWeb object at SPSite.RootWeb. Once you have the top-level site’s SPWeb object, you can iterate through each of the child sites by accessing the SPWebCollection object at SPWeb.Webs. It’s possible to retrieve a particular site in the SPWebCollection by using an indexer, which can be either the index number of the site within the site collection, the display name of the site, or the GUID of the SPWeb object. The four listings that follow are included in the SitesAndWebs.cs section of the code download at Wrox.com. Listing 8-1 shows how you can retrieve a site titled Subsite A, where Subsite A is a direct child of the top-level site.

image Listing 8-1: Getting an SPSite Object (SitesAndWebs.cs)

SPSite siteCollection = SPContext.Current.Site;

SPWeb topLevelSite = siteCollection.RootWeb;

SPWebCollection webs = topLevelSite.Webs;

SPWeb subsiteA = webs["Subsite A"];

Whereas accessing the Webs property of an SPWeb object will return the direct child sites of a given site, the AllWebs property of the SPSite object will return a collection of all the sites in a site collection, regardless of how deep they lie in the site collection hierarchy. Figure 8-3 shows which sites are returned in a Webs collection and in an AllWebs collection.

Listing 8-2 shows you how you can iterate through all the sites in a site collection using the SPSite.AllWebs property.

image Listing 8-2: Creating a New SPSite Object (SitesAndWebs.cs)

using (SPSite intranetSiteCollection = new SPSite("http://intranet/"))

{

    SPWebCollection websCollection = siteCollection.AllWebs;

 

    foreach (SPWeb web in websCollection)

    {

        Console.WriteLine(web.Title);

        web.Dispose();

    }

 

    Console.Read();

}

image

Note that when retrieving SPSite or SPWeb objects directly from the SPContext.Current object, you should not explicitly dispose of the objects, which is why you don’t see a “using” block or Dispose() method in Listing 8-1. Since the SPContext object is continually used, disposing of this object will produce significant errors. However, when creating a new SPSite object “from scratch,” you do need to explicitly dispose of it when finished. In Listing 8-2, the SPSite object will be disposed of properly because of the “using” block it is a part of. Each SPWeb object that’s retrieved from the AllWebs property is also being disposed of explicitly, this time using the Dispose() method.

Once you have a site collection object, you can also retrieve a particular site in the site collection, even if it’s more than one level deep, by using the SPSite.OpenWeb() method. This method allows you to retrieve a particular site within the collection by passing in the GUID or the URL of the site (or retrieve the top-level site by passing in no parameters). Listing 8-3 demonstrates how you can get the top-level site of a site collection (http://intranet), as well as retrieve the SPWeb object for a site that’s a grandchild of the top-level site.

image Listing 8-3: Using OpenWeb() (SitesAndWebs.cs)

using (SPSite sc = new SPSite("http://intranet/");

{

    SPWeb rootWeb = sc.OpenWeb();

    rootWeb.Dispose();

 

    SPWeb web = sc.OpenWeb("subsite1/subsite2");

    web.Dispose();

}

Once you have retrieved an object representing the site whose data you want to use, you can start retrieving information about the lists, list items, folders, and files within that site.

To access a list object for a particular list in your site, you simply need to pass in the display name or GUID of the list to the SPListCollection indexer of the parent SPWeb.Lists property, like this:

SPListCollection lists = subsiteA.Lists;

SPList myList = lists["My List"];

You can retrieve a specific item from a list in much the same way. You can pass in the value of the Title column of a particular list item in order to retrieve it, like this:

SPListItem myListItem = myList["My List Item"];

You can also use the SPListItemCollection.GetItemById() method to retrieve a particular list item by its ID number. (This is the SPListItem.ID property, not the SPListItem.UniqueIdentifier property. See side note.)

image

SPListItem objects have two identifiers: the ID property is an integer that uniquely identifies the list item. It’s possible to display a list item’s ID when creating a new view of a specific list or library. The UniqueIdentifier property is a GUID used to identify the list item. This value is rarely, if ever, visible to end users.

Although it’s possible to retrieve information from SharePoint, it’s also possible to make changes to these objects and persist your changes to the content database. To do this, it’s usually necessary to call the Update() method of the object you’re working with, which commits the changes you have made in your code to the database. The following code snippet demonstrates how you can update the URL of the master page for a given site:

SPWeb currentWeb = SPContext.Current.Web;

currentWeb.MasterUrl = "/_catalogs/masterpage/Custom.master";

currentWeb.Update();

In much the same way as you would retrieve an item from a collection, you can often add an item to a collection using an Add() method, whether it’s SPWebCollection.Add(), SPListCollection.Add(), or SPListItemCollection.Add(). Each object has its own set of overloaded Add() methods. For instance, you can easily add a new child site to an existing site by simply passing in the URL of the new site, and you can create a new list by passing in the name and description of the new list and the type of list that you want to create, as demonstrated in the next code snippet. To add a new list item to a list, you need to first add the item, set its properties, then call the SPListItem.Update() method to commit the item to the database.

SPWeb newWeb = SPContext.Current.Web.Webs.Add("MyNewSubsite");

SPList newList = newWeb.Lists.Add("New List", "Another list",

    SPListTemplateType.GenericList);

SPListItem newListItem = newList.Items.Add();

newListItem["Title"] = "New List Item";

newListItem.Update();

Not only can you add items to a collection or update an existing item in a collection, but you can also delete an item from a collection. This is usually done using the Delete() method for an object, such as SPSite.Delete(), SPWeb.Delete(), SPList.Delete(), and SPListItem.Delete(). The next example demonstrates how you can delete a site collection, site, list, and list item:

//Deletes a site collection used for testing

using (SPSite siteCollection = new Site("http://intranet/sites/test"))

{

    siteCollection.Delete();

}

 

//Deletes a site in the current site collection

using (SPWeb testSite = SPContext.Current.Site.OpenWeb("test"))

{

    testSite.Delete();

}

 

//Deletes a list called "Invoices" in the current site

SPContext.Current.Web.Lists["Invoices"].Delete();

 

//Deletes a list item with a title of "My List Item"

SPList myList = SPContext.Current.Web.Lists["My List"];

myList["My List Item"].Delete();

It’s possible to execute code during particular events such as websites, lists, or list items being added or deleted. To learn more about event receivers, read the section titled “Example: Taking Advantage of Event Receivers” in Chapter 17.

Example: Creating a SharePoint Console Application

The goal of this example is to walk you through the process of writing a console application, which will access SharePoint using the server-side object model, using the kind of code you have reviewed so far in the chapter. It will walk you through the creation of a console application that you can use to return the titles of all the sites within a site collection. This example can be downloaded from the Wrox.com site. The project is called SP2010ObjectModel.

There are two things to note when creating a new console application that will access SharePoint 2010:

  • At the time of the printing of this book, SharePoint 2010 does not utilize the .NET Framework 4.0. You will need to target your Visual Studio project to .NET Framework 3.5.
  • By default, your project might target a 32-bit processor. Because SharePoint targets a 64-bit processor, it’s necessary to set your Visual Studio project deployment target to x64.

Here are the steps in the process:

1. Open Visual Studio 2010 and select New ⇒ Project from the File menu.

2. On the left side of the New Project window, find the heading titled Installed Templates. Under the heading, click the item titled Visual C#. Select the Console Application project template. In the drop-down menu above the center column, select .NET Framework 3.5 as the framework that your project will target. In the Name textbox at the bottom of the window, type “SP2010ObjectModel.” Click OK.

3. Right-click the project and select Properties. Click the Build tab and select x64 from the Platform target drop-down menu. Save your changes.

4. Right-click the References folder and select Add Reference. Navigate to the SharePoint root folder. Open up the ISAPI folder and select Microsoft.SharePoint.dll, then select OK.

5. Open up Program.cs and add the following “using” statement to the top of the class: using Microsoft.SharePoint;

6. Add the code from Listing 8-2 to your Main() method, substituting http://intranet with the URL of your site collection.

7. Save your file and run your application using the account of a user who has access to the site collection that you are querying. A command window should pop up and display the names of all the sites within the site collection whose URL you supplied.

Working with Folders and Files

Folders are used in SharePoint to denote a logical collection of items, in much the same way you might use a folder on your computer to store documents. As with your desktop folders, a folder can contain other folders. Folders can serve several purposes in SharePoint:

  • Each list can contain folders for organizing list items within that list. These kinds of folders are visible to end users who are browsing the list, as shown in Figure 8-4. For SharePoint, a folder in a list is actually a list item in and of itself, and as such has an associated content type as well. Folders in document libraries can contain files. You can see in Figure 8-5 how the December folder in the Documents library contains a Word document called MyDocument.doc.
  • Folders can be a container for holding files. However, they are not necessarily visible to users in the browser. You can see some of these folders when you open up a site in SharePoint Designer. For example, master pages are stored in a folder called _catalogs in the top-level site of your site collection, as shown in Figure 8-5.
  • Every list is also a folder, with that folder having the same name as the URL of the list. For instance, a document library called Documents also is a folder. If a list is a library, it usually contains a child folder called “Forms” that is used for storing files like the “new,” “edit,” and “display” forms for that list, as well as content-type document templates. The Forms folder is not visible to users in the browser. You can see the Forms folder in Figure 8-5, as seen in SharePoint Designer. The list folder also contains any folders that have been created in the browser and are visible to end users, as mentioned in the first bullet point.

Understanding Folder and File Objects

A folder in SharePoint is represented by the SPFolder object. It’s possible to retrieve a collection of all a folder’s child folders by accessing the SPFolder.SubFolders property, which returns an object of type SPFolderCollection. If the folder is contained inside another folder, it’s possible to retrieve the parent folder by accessing the SPFolder.ParentFolder property.

Listing 8-4 shows how you can retrieve the name of each folder in the top-level site of a site collection, as well as the number of subfolders that each folder contains.

image Listing 8-4: SPFolder.cs

using System;

using System.Text;

using Microsoft.SharePoint;

 

namespace Wrox.SixInOne

{

  class Program

  {

    static void Main(string[] args)

    {

      StringBuilder folderName = new StringBuilder();

 

      using (SPSite site = new SPSite("http://intranet/"))

      {

        using (SPWeb web = site.RootWeb)

        {

          foreach (SPFolder folder in web.Folders)

          {

            folderName.Append(folder.Name);

            folderName.Append(" ");

            folderName.Append(folder.SubFolders.Count);

            folderName.Append("<br>");

          }

        }

      }

    }

  }

}

Folders in SharePoint can contain one or more files. A file can be any kind of file, such as an ASPX page, an image, or a document. A list item in a list is not a file in and of itself. However, if the SharePoint list is actually a library (such as a document library, image library, and so on), each item in that library also has an accompanying file. Additionally, if a list item allows attachments, any file attached to the list item would also be considered a file.

Files are represented by the SPFile object, and as you can probably guess by now, the collection of files belonging to a folder can be returned by accessing the SPFolder.Files property, which is of type SPFileCollection. You can also find out which folder a file belongs to by accessing the SPFile.ParentFolder property.

Deleting a file or a folder is pretty straightforward; you can simply call SPFolder.Delete() or SPFile.Delete(). However, in some cases you might prefer to send the folder or file to the recycle bin, rather than delete it permanently from the system. You can do that by calling SPFolder.Recycle() or SPFile.Recycle().

Adding a new folder programmatically to an existing folder is as simple as using an Add() method that passes in the name of the folder, like this:

SPContext.Current.Web.Folders.Add("NewFolder");

Adding a new file to a folder is quite a bit more difficult, because it involves uploading a binary object (such as an Excel document) to the SharePoint database. The SPFileCollection.Add() method has a number of overloaded methods, but most of them involve uploading a byte array or a data stream.

Listing 8-5 is an example of a console application that adds a new file to a list, then updates the new list item by adding a title to it.

image Listing 8-5: AddFile.cs

using System;

using System.IO;

using Microsoft.SharePoint;

 

namespace Wiley.SixInOne

{

    class Program

    {

        static void Main(string[] args)

        {

            //Instantiate a new site collection object

            using (SPSite site = new SPSite("http://intranet/"))

            {

                //Get the list object inside the subsite

                SPWeb web = site.OpenWeb("sites/HR");

                SPFolder listFolder = web.Lists["Documents"].RootFolder;

 

                string folderUrl = web.ServerRelativeUrl + listFolder.Url;

                string fileUrl = folderUrl + "/Application.docx";

 

                FileStream stream = System.IO.File.Open(@"C:Application.docx",

                    FileMode.Open);

                byte[] contents = new byte[stream.Length];

                stream.Read(contents, 0, (int)stream.Length);

                stream.Close();

 

                SPFile file = web.Files.Add(fileUrl, contents);

 

                SPListItem listItem = file.Item;

                listItem["Title"] = "Job Application";

                listItem.Update();

 

                web.Dispose();

            }

       }

    }

}

As you can see, the first step is to get a reference to the site collection that will be used, then get a reference to the subsite that will be used, and finally retrieve the list to which the file will be uploaded. To upload a file to SharePoint, you need to tell SharePoint where you want the file to go (that is, into which site, list, folder, and so forth it will be placed). You can do this by passing in the URL of the new document, which maps to the location of the file within the site. In the following example, you’ll tell SharePoint that you want the new file to be placed in the Documents document library in the HR subsite, and you want the document to have a name of Application.docx. That means that the final URL of the new document should be http://intranet/sites/HR/Documents/Application.docx.

Once you’ve determined where you want your new document to go and what you want the document to be called, you’ll serialize the document you want to upload into a byte array, then tell SharePoint to take that byte array and create a new file within SharePoint at the location you specified. Simply uploading a file to a document library automatically creates a new list item, but if that’s all you do, your new list item won’t have any metadata (that is, no custom column values) assigned. To add metadata, you’ll get a reference to the new list item that was just created and add metadata, such as a document title, then update the new list item with your changes. In this example, the new list item will be given a title of “Job Application.”

The Relationship between Folders and Lists and between Files and List items

Imagine, for a moment, a blue truck. How could you categorize it? You could say that the vehicle is a truck, whereas other vehicles on the road are cars and motorcycles. You could also say that the truck is blue, whereas other vehicles are white or red. The point is that the vehicle is more than one thing at once. The same goes with lists, list items, folders, and files. Because there is an overlap, the relationship with them can be confusing. A couple guidelines may help you out.

First, every list is a folder, but not every folder is a list. If you had a list called Events, then your site would have a folder called Lists, and that folder would contain a folder called Events. The folder called Lists is not a list itself, but is simply a folder in the organizational sense of the word. However, the folder called Events also corresponds to the list called Events. It would not be possible to have a list called Events without that list also being a folder called Events. You can see the Lists folder and the Events list/folder in Figure 8-6.

If you are working with the SPList object for a given list, you can access the list’s folder object by calling the SPList.RootFolder property. This will return an SPFolder object that you can work with. If you are working with an SPFolder object, it might be that the folder is inside a list, but it’s also equally possible that the folder is not inside a list. To find out, you can access the ParentListId property on the SPFolder object. If the GUID that is returned is all zeros, then the folder is not inside a list; otherwise, the GUID of the list is returned. Because every folder must belong to some site, accessing the SPFolder.ParentWeb will always return an SPWeb object representing the site that contains the folder.

There is a similar overlap when dealing with files and list items. A file can be associated with a list item, but not necessarily. A list item might have a file, but not necessarily. For instance, a list might have an ASPX page that contains the form for creating new list items. That file is associated with a list, but not a list item per se. A list item might actually be a document (because the list is actually a type of library), or it might have an attachment file. However, if a list is not a document library so the list item has no accompanying document, and the list item has no attachment, there would be no file associated with the list item.

The SPFile.Item property returns the list item associated with a file if there is one. So if your file is actually a document in a document library, the SPFile.Item property would return the SPListItem object that your file is a part of. However, if your file is not associated with a list item (as in the example of the input form), the property value would be null. If you were accessing an SPListItem object and the list item was a document, accessing SPListItem.File would return an SPFile object representing the document. If the SPListItem had no document, this value would be null. If you wanted to access any attachment files associated with the list item, you could access the SPListItem.Attachments property, which would return a collection of SPFile objects that are attached to the list item.

AN INTRODUCTION TO COLLABORATIVE APPLICATION MARKUP LANGUAGE (CAML)

Collaborative Application Markup Language, or CAML, is an XML language that is used for defining certain assets within SharePoint, such as site columns, content types, site definitions, list definitions, list views, and list instances.

According to the dictionary, the noun “imperative” means “a command.” Executing code that you have written using the SharePoint object model is often referred to as imperative programming, because you are executing commands at a point in time. For instance, you can use the SharePoint object model to create a new instance of a list in a site, but the new list will not be created until your code is executed.

In contrast, CAML is considered declarative programming, because it “makes known” or “explains” how various objects are to be configured within SharePoint, once they are created. An object such as a field or a list that has been described using CAML still needs to be created at some point in time in order to be useful, but the CAML file itself can serve as a succinct way of telling SharePoint how that object should be configured when it’s first created. Or it can serve as an ongoing configuration definition that SharePoint can refer back to over the course of an object’s life within SharePoint.

Many of the activities in SharePoint can be carried out either imperatively (using code) or declaratively (using CAML), and in many cases the end result is exactly the same. Many people want to know whether one method is the “right” way to do things, but in most cases both roads get you to the same destination.

For example, you could use the SharePoint server object model to create a new Yes/No column, like this:

using (SPSite siteCollection = new SPSite("http://intranet/"))

{

    SPWeb rootWeb = siteCollection.RootWeb;

    rootWeb.Fields.Add("New Field", SPFieldType.Boolean, true);

}

You could create a new feature and add this code to the FeatureActivated method in the feature event receiver. When the feature is activated, it will execute the code and add a new Boolean field with a display name of New Field to the site collection. (For an explanation of what a feature is and how to use one, please see Chapter 7: Features and Solutions.)

You could alternatively create a new feature that included an element manifest file. Inside the element node in the element manifest file, you could add the following CAML:

<Field ID="{6E43255B-14CF-49c5-9331-A8BC65921140}"

Name="NewField"

DisplayName="New Field"

Type="Boolean"/>

When this feature is activated, SharePoint will execute a command to create a new field, but it will use the CAML file to configure the new field, giving the field the ID, internal name, display name, and type, defined in the Field element in the CAML.

An important difference between imperative and declarative programming that you need to keep in mind, is that when you program imperatively the same code could be executed multiple times. If the code adds a new field to your site collection, you could get an error if that field already exists, or you could end up with duplicate fields. You need to make sure your code has proper validation and error handling before performing an operation like adding or modifying an item. In contrast, SharePoint determines when and how CAML will be used. If you create a feature that uses CAML to define a new field, the first time you activate the feature, the new field will be created. However, if you activate the feature again, if that field already existed in the site, SharePoint won’t provision a duplicate version of that field or throw an error saying that the field already exists.

CAML is also used to determine the configuration of list views. An important part of defining a view on a list is the ability to filter and sort data in that list. You can do this by defining “where,” “group by,” and “order by” statements, just as you might do when querying a database using T-SQL. As an extension of this kind of functionality, it’s possible to execute a CAML query independent of a list or a list view. A CAML query allows you to retrieve specific list items directly from the SharePoint database, by passing in the filtering, ordering, and grouping criteria of your query.

Working with Fields, Content Types, List Definitions, and List Instances Using CAML

One of the most common ways you might use CAML is to provision something new within SharePoint, whether this is creating a new column, adding a new list or file to a site, or making a new list definition available. You’ll probably use a feature to provision your item, which means you need to create a new feature that contains your CAML markup. (About the only time you won’t use a feature to use your CAML markup is when you create a site definition. Site definition CAML is used when a new site is created using a custom site definition.) Because lists usually include content types, and content types usually include columns, it’s important to understand how columns, content types, and lists can build upon one another using CAML.

The first step you need to take after creating your new feature is to create an element manifest file, which you will reference in your feature. When your feature is activated, SharePoint will automatically read the CAML defined in your element manifest file(s) and provision the items defined there. All your CAML markup will be contained within the <Element> node of the element manifest. (For more information about what features are and how to configure them, see Chapter 7.)

Because CAML can be used to configure numerous kinds of SharePoint objects and each of those objects’ properties, the volume of nodes, child nodes, and attributes that can be used within CAML is extensive and can in no way be described in its totality in this chapter. For a complete explanation of CAML, read the SDK documentation of CAML at http://msdn.microsoft.com/en-us/library/ms462365.aspx.

The next section walks you through a few of the most commonly used CAML elements.

Field Element

The Field element defines a column. If the Field element has been added directly to an element manifest file, the site column gets directly added to the site collection where the feature is activated. If the Field element is used in a list definition, the column will be provisioned as part of that list. Each Field element must have an ID, whose value is a globally unique identifier, as well as a Type property, which identifies what kind of column will be created.

image

When a site column that has been added to a site collection is added to a list within that site collection, SharePoint actually creates a copy of that column and adds it to the list. The column will have the same ID as the site column, but is actually a copy of, not a reference to, the site column. If you update a site column in the site collection, you have a choice about whether you want to push your changes to all the lists that have a copy of the column. If you choose to propagate the changes, SharePoint will find every copy of that column with the same ID in the site collection and update it with the changes. If you don’t propagate the changes, SharePoint simply leaves the copied columns with the same ID alone. After you’ve made changes to your site column, each time that column is added to a list, the list will use the modified column. In this way, it’s possible for new lists to use the new column configuration, while allowing existing lists to remain as is.

Every column has at least two name values: an internal name and a display name. Although this could potentially cause confusion, it can be helpful in certain scenarios. Imagine you want to create a column called Day of Week, which is a column of type Text. You want to use the column to store the name of a weekday, such as Tuesday, in a list. However, imagine that you have a list within the site where you also want a column called Day of Week, but in this case, you want to store a numerical value from 1 to 7, which represents which day of the week something takes place. You want to deploy both columns to your site collection; however, you can’t have two columns with the same name but different types. In such a scenario, it’s possible to create one column with an internal name of DayOfWeekText and another with an internal name of DayOfWeekNumber, while giving both of them a display name of Day of Week.

image

If you create a column with an internal name that includes spaces, such as Column Name, SharePoint will automatically convert those spaces to hexadecimal format, which means a non-breaking space will be converted to the text _x0020_. Once this happens, you will always need to refer to your column using the hexadecimal notation, which means you would need to refer to your column title Column Name with an internal name of Column_x0020_Name. To avoid this scenario, simply create your column with an internal name that doesn’t include spaces, and give your column a display name that includes spaces. When creating columns via the browser, you can do the same thing by creating your column the first time without spaces, (which generates an internal name without spaces), and modifying the column name to include spaces (which will modify the display name of the column).

Listing 8-6 shows an example of several Field elements.

image Listing 8-6: Fields.xml

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

    <Field ID="{C65F47C9-4358-48ae-BB03-79E8F5EEC0FA}"

        Name="PublicationHeadline"

        DisplayName="Publication Headline"

        Group="My Custom Columns"

        Type="Text"/>

        <Default>Quarterly News</Default>

    </Field>

    <Field ID="{F792E8FA-1464-4cec-BA66-75DC784DC132}"

        Name="DisplayOnHomepage"

        DisplayName="Display on Homepage"

        Group="My Custom Columns"

        Type="Boolean"/>

    <Field ID="{4BEC2FC9-51D0-4f11-B99F-2D01359FAE9C}"

        Name="PubicationNumber"

        DisplayName="Publication Number"

        Group="My Custom Columns"

        Type="Number"

        Min="0"

        Decimals="0"/>

    <Field ID="{071B0E41-900C-47e3-ACB3-F9A03ED45023}"

        Name="PublicationDate"

        DisplayName="Publication Date"

        Group="My Custom Columns"

        Type="DateTime"

        Format="DateOnly"/>

    <Field ID="{0DFD4BB2-DBC8-461b-8A2E-E7E5D077F679}"

        Name="FiscalQuarter"

        DisplayName="Fiscal Quarter"

        Group="My Custom Columns"

        Type="Choice">

        <CHOICES>

            <CHOICE>Q1</CHOICE>

            <CHOICE>Q2</CHOICE>

            <CHOICE>Q3</CHOICE>

            <CHOICE>Q4</CHOICE>

        </CHOICES>

    </Field>

</Elements>

Notice that the Group attribute used with each Field element tells SharePoint the group heading under which the column will appear on the Site Columns administration page. Notice also that each different field type has its own attributes such as Min and Decimals for Number columns, Format for DateTime columns, and so on. Read the Field element SDK article at http://msdn.microsoft.com/en-us/library/aa979575.aspx for detailed information regarding type-specific attributes for specific field types.

Visual Studio 2010 does not provide any sort of import functionality for defining a site column in CAML the way it provides functionality for importing content types (as you will read about in the next section). In order to define your site columns, you’ll need to add a new element file to your SharePoint solution and manually add your Field elements to it.

Content Type Element

The ContentType element defines a content type. Like a column, this will add a content type to the top-level site of a site collection unless it has been defined within a list definition instead.

Unlike fields, content types can inherit from one another. For instance, a calendar event content type is actually a type of list item content type, whereas a picture in a picture library is a type of document content type. SharePoint content type IDs reflect their inheritance structure. To start with, every single item contained in any list in SharePoint has a content type ID that beings with 0x01. A document is a kind of list item, so its content type ID begins with the list item’s ID, but then it adds its own additional ID numbers, like this: 0x0101. An XML document is a kind of document, so it uses the content type ID of the document content type, but then adds its own numbers onto the end, too: 0x010101. (If you want to see some of the out-of-the-box content type IDs, view the file found in your SharePoint root at ~/14/TEMPLATE/FEATURES/ctypes/ctypeswss.xml.) When you want to add a new content type that inherits from a parent content type (which all content types must), simply append two zeros (“00”) to the end of the content type ID that you want to inherit from, and then add a new GUID to the end of it, removing the dashes and curly brackets from the GUID. For instance, to create a new content type that inherits from the document content type, you would follow this formula:

0x0101 + 00 +

{7F4D3961-97EA-4D68-AA90-F23EC36D0A1F} -

{----} =

0x0101007F4D396197EA4D68AA90F23EC36D0A1F

To prevent a content type ID from getting too long, it’s possible, after having created the initial inherited ID as seen in the preceding formula, to start adding just two additional numbers to the end of that content type ID, to indicate inheritance. For instance, if you want to create a parent content type that every document in your site will inherit from, you can give it the content type ID defined in the previously mentioned formula. Then you could create a new Policy content type that inherits from your custom content type, by giving the Policy content type an ID of 0x0101007F4D396197EA4D68AA90F23EC36D0A1F01. If you create a Procedure content type that also inherits from your custom document content type, the Procedure content type ID can be 0x0101007F4D396197EA4D68AA90F23EC36D0A1F02, and so on.

One of the primary reasons for using content types is so you can create collections of columns that can be used together. For instance, an Event content type uses columns such as Start Time, End Time, Location, and so on. To include columns in your ContentType element, you will add child FieldRef elements, which reference site columns that have already been provisioned.

Listing 8-7 shows an example of a ContentType element that uses the columns defined in Listing 8-6.

image Listing 8-7: ContentType.xml

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

    <!-- Parent ContentType: Document (0x0101) -->

    <ContentType

        ID="0x01010025f589d93b3b41ac93c3e91064b18358"

        Name="Publication"

        Group="My Content Types"

        Description="Quarterly publication document">

        <FieldRefs>

            <FieldRef Name="PublicationHeadline"

                ID="{C65F47C9-4358-48ae-BB03-79E8F5EEC0FA}"/>

            <FieldRef Name="DisplayOnHomepage"

                ID="{F792E8FA-1464-4cec-BA66-75DC784DC132}"/>

            <FieldRef Name="PublicationNumber"

                ID="{4BEC2FC9-51D0-4f11-B99F-2D01359FAE9C}"/>

            <FieldRef Name="PublicationDate"

                ID="{071B0E41-900C-47e3-ACB3-F9A03ED45023}"/>

            <FieldRef Name="FiscalQuarter"

                ID="{0DFD4BB2-DBC8-461b-8A2E-E7E5D077F679}"/>

        </FieldRefs>

    </ContentType>

</Elements>

Content types do not have internal and display names the way columns do. Notice that you can assign a group name to your content type, so that your content type will appear under that group name on the Content Type administration page, just as you would add a group name to your column.

It’s fairly easy to create a new content type element in Visual Studio. First, create a new Visual Studio project and point that project to your development SharePoint site. Add a new item to your project, using the SharePoint 2010 Content Type project item template. When you do so, SharePoint will contact the development site and will show you all the content types that are installed in that particular site collection. Choose the content type that you want your new custom content type to inherit from, and SharePoint will automatically generate the content type CAML for you. Keep in mind that if you want to add columns to the content type that are not already installed in the site collection, you must be sure to add the custom columns to your solution (as described in the previous section) and then reference them in your new content type.

List, List Template, and List Instance Elements

A list schema defines the structure of a kind of list. One or more definitions can be created from that schema, and one or more instances of a list can be generated from that definition. For example, if you want to create a new document library in your site, you might select the Documents list template. However, you might create two instances using that template: one called Company Documents and another called Departmental Documents. They both use the same definition, but they are two different libraries.

Figure 8-7 demonstrates the one-to-many relationship between list schemas, definitions, and instances.

A list’s schema must always be contained in a file called Schema.xml. If the file is not named Schema.xml, SharePoint will not be able to find it, and will throw an error if you try to provision or use the list. The Schema.xml file contains a single List element. That list element can be used to define properties on the list that would be created from it, such as:

  • Is versioning enabled?
  • Are attachments allowed?
  • Can users create folders in the list?
  • What content types are a part of this list?
  • Which columns are included in this list?
  • Which views are available on this list, which columns are part of these views, and how are the views grouped, sorted, filtered, or paged?

Listing 8-8 shows a very basic list schema.

image Listing 8-8: Schema.xml

<?xml version="1.0" encoding="utf-8"?>

<List xmlns:ows="Microsoft SharePoint" Title="CustomList"

  FolderCreation="FALSE"

  BaseType="0"

  xmlns="http://schemas.microsoft.com/sharepoint/">

  <MetaData>

    <ContentTypes>

      <ContentTypeRef ID="0x01">

        <Folder TargetName="Item" />

      </ContentTypeRef>

      <ContentTypeRef ID="0x0120" />

    </ContentTypes>

    <Fields></Fields>

    <Views>

      <View BaseViewID="0" Type="HTML"

        MobileView="TRUE" TabularView="FALSE"

        DisplayName="All Items" Url="AllItems.aspx"

        DefaultView="TRUE">

        <Toolbar Type="Standard" />

        <XslLink Default="TRUE">main.xsl</XslLink>

        <RowLimit Paged="TRUE">30</RowLimit>

        <ViewFields>

          <FieldRef Name="LinkTitleNoMenu"></FieldRef>

        </ViewFields>

        <Query>

           <OrderBy>

            <FieldRef Name="Modified" Ascending="FALSE"/>

           </OrderBy>

        </Query>

      </View>

    </Views>

    <Forms>

      <Form Type="DisplayForm" Url="DispForm.aspx"

        SetupPath="pagesform.aspx" WebPartZoneID="Main" />

      <Form Type="EditForm" Url="EditForm.aspx"

        SetupPath="pagesform.aspx" WebPartZoneID="Main" />

      <Form Type="NewForm" Url="NewForm.aspx"

        SetupPath="pagesform.aspx" WebPartZoneID="Main" />

    </Forms>

  </MetaData>

</List>

Each list definition must also have an element manifest containing a ListTemplate element that tells SharePoint where to find the list definition file. The ListTemplate element gives the list definition an ID (which will be used when instantiating instances of the list), and gives SharePoint additional information about properties of the list definition. Listing 8-9 shows a sample ListTemplate element.

image Listing 8-9: ListTemplate.xml

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <ListTemplate

    Name="PublicationsListDefinition"

    Type="10000"

    BaseType="1"

    OnQuickLaunch="TRUE"

    SecurityBits="11"

    Sequence="110"

    DisplayName="Publications"

    Description="My List Definition"

    Image="/_layouts/images/itdl.png"

    DocumentTemplate="121"/>

</Elements>

Some of the properties assigned to the ListTemplate element might have already been defined in the Schema.xml file, but the ListTemplate element overrides those values. The idea is that you could create more than one list definition based on the same schema. For instance, you could have a list definition called Publications defined in a Schema.xml file. You could create one ListTemplate element that has versioning enabled and another that doesn’t. When users try to create a new list, they would see one list template called Publications with Versioning and another called Publications without Versioning. Both of these list templates reference the same underlying schema and include the same columns, content types, views, and so on, but they are configured slightly differently. To sum up, the List element provides a base list definition, and the ListTemplate element tells SharePoint which definitions should appear in the browser when a user tries to create a new list or library (as shown in Figure 8-8).

List definitions can be incredibly difficult to define manually. Luckily, you rarely have to. To create a custom list definition in Visual Studio, you can use one of two SharePoint 2010 project item templates. The List Definition project item template will ask you which kind of list you would like to create (such as document library or announcement list), then will generate a list definition schema for you, which you can modify as necessary. If you would like your list to be preconfigured to contain a particular content type and that content type’s columns, you can select the List Definition from Content Type project item template. This template will show you all the content types that are currently installed on your development SharePoint site, and will ask you to select a content type to base your list definition on.

To generate an instance of a list, you will use the ListInstance element. This element tells SharePoint which list template to use, as well as information such as the display name, description, and URL of the new list. Listing 8-10 shows a sample ListInstance element.

image Listing 8-10: ListInstance.xml

<ListInstance Title="Publications"

    OnQuickLaunch="TRUE"

    TemplateType="10000"

    Url="Lists/Publications"

    Description="List of publications">

</ListInstance>

You have two simple ways to generate a list instance in Visual Studio. If you create a list definition in Visual Studio using one of the aforementioned project item templates, you can instruct Visual Studio to include a list instance in the Visual Studio project at that time. You can also add a list instance to your project by using the List Instance Visual Studio SharePoint 2010 project item.

When creating list templates and list instances, remember that they will most likely be deployed using a feature, and that this feature will have a scope assigned. List definitions must be deployed to a site collection. If you include a list instance in the same feature where you deploy your list definition, it means that when you activate that feature in your site collection, it will also provision an instance of that list in the root of your site collection. Furthermore, because the feature has already been activated, it will not be possible to activate that feature in any other site in the site collection. For that reason, it’s sometimes helpful to create one feature with a scope of Site, which deploys your list definition, and to create another feature with a scope of Web, which provisions an instance of that list. When your features are constructed in such a way, your list definition only needs to be provisioned once in your site collection, but you can create a new instance of your list in any site within the collection by activating the feature scoped for just that site.

image

When you are creating a new list definition from scratch, it’s not unusual to include an element manifest that provisions your site columns, another element manifest that provisions your content types based on the site columns defined in the first element manifest, and finally to create a list definition based on the content type you defined. Remember that a feature will process its constituent element manifests in the order in which they are listed in the feature. Make sure your element manifests, list definitions, and list instances are listed in the proper order in your feature. Furthermore, if you have separate features for your list definition and list instance, make sure your list instance feature has an activation dependency on the list definition feature.

Module Element

The Module element allows you to add files to a SharePoint library. Modules are often used for adding files like master pages to the Master Page gallery, or cascading style sheets to the Style library. Listing 8-11 shows a sample Module element that is adding a master page to a Master Page gallery.

image Listing 8-11: Module.xml

<Module Name="Masterpage" Url="_catalogs/masterpage">

    <File Path="masterpageCustom.master"

        Url="Custom.master" Type="GhostableInLibrary">

        <Property Name="Title" Value="AECI Intranet Master Page" />

        <Property Name="MasterPageDescription"

            Value="Custom intranet master page." />

        <Property Name="ContentType" Value="Master Page" />

    </File>

</Module>

The Module element allows you to specify the path to the file on the server (in your Feature folder) where the files you want to add to SharePoint live, and the URL of the location in SharePoint to which you want to add the file. Module elements contain one or more child File elements. The File element tells SharePoint which specific file should be added to SharePoint, along with any properties the file might have.

Files where the Type property is set to Ghostable or GhostableInLibrary are considered un-customized (also known as “ghosted”); that is, until the file is modified using a tool like SharePoint Designer, whenever a user views that file in the browser, SharePoint will actually utilize the file in the Feature folder on the web server rather than a file stored in the content database to show that file to the user. If you are deploying a file that is to be a part of a list whose list definition is a kind of document library, then you will set this property to GhostableInLibrary; otherwise, you can set this property to Ghostable.

You can download a Visual Studio project on Wrox.com that deploys the Publication site columns, content type, list definition, list template, and list instance to your site collection. The project is called Wrox.SixInOne.CAML.

CAML Queries

Iterating through list data utilizing the server-side object model requires a certain amount of server resources. For each list item that SharePoint retrieves, it must create an SPListItem object and populate that object with all the column values for that list item. If you wanted to retrieve a batch of items from a list or even a whole site collection, it could be very resource-intensive to loop through every single item to find just those particular items you’re looking for. SharePoint provides a more efficient way of querying the content database, requiring lower overhead. This is done using CAML queries. Basically, a CAML query is like a typical T-SQL or LINQ statement, in the sense that you can use keywords such as “select,” “where,”“order by,” and “group by.” In SharePoint 2010, it’s now possible even to create a query that uses a join statement to join more than one list in a query, just as you might use a join statement in a T-SQL query.

CAML queries are actually used to define the views available on lists as well as a means for you, the developer, to perform queries against data stored in SharePoint. The next section will focus on the latter topic.

Understanding the CAML Query Syntax

If you are using CAML to return only certain items from a list, to be used in a particular view that you’re defining in a list definition, your query must start with <Query/> tags. If you are using the CAML in a query object, you will omit this tag.

You’ll want to define three basic things:

  • The criteria of the list items you want returned in your query, defined in your Where tags
  • How you want your results sorted, defined in your OrderBy tags
  • How you want your results grouped, defined in your GroupBy tags

The following snippet shows a simple CAML query that returns all the documents whose content type is Policy Document:

<Where>

    <Eq>

        <FieldRef Name='Content Type'/>

        <Value Type='Text'>Policy Document</Value>

    </Eq>

</Where>

Inside your Where clause, you can specify multiple criteria that can be met. If all the criteria must be met, you will use the And operator. If either of the criteria must be met, you will use the Or operator.

Your query usually revolves around finding list items that have a particular field value, or a set of column values. You can compare these column values by passing in operators such as “equals,” “greater than,” “less than,” “is null,” “is not null,” “begins with,” “contains,” and the like. These are represented by tags such as <Eq/>, <Gt/>, <Lt/>, <IsNull/>, <IsNotNull/>, <BeginsWith/>, <Contains/>, and so on.

Finally, your comparison operators will be comparing field values and some other value. Field values are represented by the <FieldRef/> tag, similar to what you use when referencing columns in content types.

CAML queries can be nested, which can make them confusing to read. The following example demonstrates the CAML representation of a statement such as “Select all the list items in the site collection where the content type is equal to ‘Policy Document’ and the Created date is more than one year old. Sort the results so the oldest document is first and group the results by the policy number.”

<Where>

    <And>

        <Eq>

            <FieldRef Name='Content Type'/>

            <Value Type='Text'>Policy Document</Value>

        </Eq>

        <Gt>

            <FieldRef Name='Created'/>

            <Value Type='DateTime'>

                <Today OffsetDays='-365' />

            </Value>

        </Gt>

    <And>

</Where>

<OrderBy>

    <FieldRef Name='Created' Ascending='TRUE'/>

</OrderBy>

<GroupBy>

    <FieldRef Name='PolicyNumber'/>

</GroupBy>

Because a query such as this can be difficult to both write and read, it can be advantageous at times to utilize LINQ to SharePoint, which will be covered later in this chapter.

For another example of a CAML query in use, see the section titled “Example: Building the GetItems Web Part” later in this chapter.

Using the SPQuery and SPSiteDataQuery Objects

Sharepoint provides two query objects that use CAML queries. These are SPQuery and SPSiteDataQuery. SPQuery can query only a single list whereas SPSiteDataQuery can be used to query data throughout a site collection. Both objects have a Query property that accepts CAML query text as a string value.

You can create an SPQuery object and pass it in as a parameter to the SPList.GetItems() method to retrieve all the items (sorted and grouped as you’ve specified) that meet the appropriate criteria in the list. The following example shows a query that returns all the list items from a list where the Title column value starts with the word “Policy.”

string camlQueryText =

    "<Where><BeginsWith><FieldRef Name='Title'/>" +

    "<Value Type='Text'>Policy</Value></BeginsWith></Where>";

SPQuery camlQuery = new SPQuery();

camlQuery.Query = camlQueryText;

SPListItemCollection items = list.GetItems(camlQuery);

The SPSiteDataQuery object works in a similar way, except you can use it to retrieve list items from multiple lists. You can choose either to retrieve items from just one site, or to retrieve items from that site and any child sites.

Both query objects also provide you with the ability to specify which fields you would like returned as well as a limit to the number of items you want returned.

AN INTRODUCTION TO LINQ TO SHAREPOINT

Language Integrated Query (LINQ) is a way of accessing external data sources using a SQL-like syntax, and has been integrated with C# 3.0 and Visual Basic 2008. To use LINQ in your code, you need a LINQ provider to provide a bridge between your code and whatever data source you are accessing. SharePoint Foundation 2010 comes with its own LINQ to SharePoint provider, contained in the Microsoft.SharePont.Linq.dll assembly. This allows us to query SharePoint lists and libraries using LINQ syntax.

One of the most compelling reasons for using LINQ to SharePoint instead of CAML is that LINQ provides strongly typed classes, whereas CAML is written as a string of text that will be executed only at runtime. Visual Studio’s IntelliSense will help you program against your lists and their columns in an easy manner, and will alert you at compile time if you are referencing a list or column incorrectly. In addition to being easier to write, LINQ provides a more readable syntax than CAML.

Using SPMetal.exe

For your lists to be strongly typed, each list must have its own representation in code. You do this by using the command line tool SPMetal.exe, which generates a class file that contains your column, content type, and list information represented in code. You can find the SPMetal tool in the bin folder in the SharePoint root folder on the server. The SPMetal tool takes a number of parameters, as outlined in Table 8-2.

Table 8-2: SPMetal Parameters

PARAMETER DESCRIPTION EXAMPLE
/web The absolute URL of the site you want to use. /web:http://intranet/sites/hr
/code The path (relative or absolute), including the name, of the file that will be generated. /code:HumanResources.cs
/code:C:HumanResources.cs
/language The programming language that the output file uses. C# is the default, otherwise you can specify VB. If the file you specified in the /code parameter has an extension of cs or vb, SPMetal can figure out which language you want to use, in which case this parameter is not needed. /language:csharp
/language:vb
/namespace The namespace used for your entity classes. If none is specified, no namespace will be used, in which case the entity classes will pick up the namespace of the Visual Studio project to which the class is added. /namespace:Wrox.SixInone.Linq
/useremoteapi This parameter has no value, but indicates that the site being used is not on the same server as SPMetal. This can be helpful if you want to create a solution package that will be uploaded to a remote server as a sandboxed solution. /useremoteapi
/user If you want SPMetal to execute as some other user, pass in the domain name and username of the user in whose context you want SPMetal to execute. /user:wroxbertram
/password The password for the username you passed in using the /user parameter. /password:J9&*!sp
/serialization If you want your objects to be serializable, specify a value of unidirectional. The default value is none. /serialization:unidirectional
/serialization:none
/parameters Path to an XML file containing additional configuration of SPMetal. /parameters:HRSiteConfig.xml

For a complete explanation of the SPMetal parameters, visit http://msdn.microsoft.com/en-us/library/ee538255.aspx.

Using these parameters, you could generate a C# file in the current location called HRSite.cs with a namespace of Wrox.SixInOne.Linq by executing the batch file in Listing 8-12.

image Listing 8-12: SPMetal.bat

CD "C:Program FilesCommon Files

    Microsoft SharedWeb Server Extensions14BIN"

SPMetal /web:http://intranet/sites/hr

    /code:HRSite.cs /namespace:Wrox.SixInOne.Linq

In general, if you execute the SPMetal tool against a particular site, SP Metal will create entity classes for each list or library in the site and one DataContext class. The entity classes are created at the time the SPMetal tool is executed, so the classes will reflect the state of the lists at that time only; the entity classes will not reflect changes made afterward to the site. One of the powerful features of SharePoint is the ease with which people can make changes to things like lists, columns, and content types, so it makes sense that you would want to use LINQ only to access content types and lists whose composition (content types or columns) don’t change on a regular basis. Otherwise, your entity classes will not reflect the true makeup of the site, and you’ll encounter errors when you try to execute your LINQ queries.

It’s possible to create a configuration file for SPMetal that tells it exactly which lists, content types, and columns you want included or excluded in the entity class that gets created. For instance, to include a list called Contacts, but to exclude all other lists in the site, you could add the following to your SPMetal configuration file:

<List Name="Contacts">

    <ContentType Name="Item" Class="Contact" />

</List>

<ExcludeOtherLists/>

You can learn how to write this configuration file by visiting http://msdn.microsoft.com/en-us/library/ee535056.aspx.

Example: Creating Entity Classes

Once you have created your entity classes file, you can reference that file in your custom code solution. This example walks you through the steps you’ll need to create this file for the first time.

Follow these steps:

1. Find a SharePoint site that you can use and create a new Customers list, using the Custom List template.

2. Create a new C# project in Visual Studio using the Empty SharePoint Project template.

3. Right-click your project and select Add Reference from the context menu.

4. Browse to the SharePoint root folder (usually located at C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions14), then open up the ISAPI directory. Select the Microsoft.SharePoint.Linq.dll assembly and add it to your project.

5. Create a new folder in your project, called LINQ.

6. Right-click the folder and select Add ⇒ New Item from the context menu. Select an item template of type Text File and give it a name of SPMetal.bat.

7. Type the following text on the first line of the text file, replacing http://siteurl with the URL of the SharePoint site you will be using:

"C:Program FilesCommon FilesMicrosoft SharedWeb Server

Extensions14BINSPMetal.exe"

    /web:http://siteurl /code:Site.cs /parameters:config.xml

8. Click the File menu, then select Save LINQSPMetal.bat As. . . . In the menu that appears, click the arrow next to the Save button, and select Save with Encoding (Figure 8-9).

9. Select “Unicode (UTF-8 without signature) – codepage 65001” encoding.

10. Right-click your project and select Properties from the context menu.

11. Click the Build Events tab.

12. In the Pre-Build Event Command Line box, type the following:

cd $(ProjectDir)/LINQ

SPMetal.bat

13. Save your changes.

14. Right-click the folder and select Add ⇒ New Item from the context menu. Select an item template of type XML File and give it a name of Config.xml.

15. Add the following XML to the file:

<?xml version="1.0" encoding="utf-8" ?>

<Web xmlns="http://schemas.microsoft.com/SharePoint/2009/spmetal">

<List Name="Customers">

       <ContentType Name="Item" Class="Customer" />

</List>

       <ExcludeOtherLists/>

</Web>

16. Build your project. You should see a new file in the LINQ folder called Site.cs. (If you can’t see the file, make sure the Show All Files button is selected in your Solution Explorer, and refresh your view of the project.) This file contains the classes generated by SPMetal from your lists. If you open up the file, you should see a class called Customers.

Using LINQ to Access Site Data

Now that you have created your entity classes, it’s time to use them in your code. The first thing you need to do is to create a new DataContext class. This class is part of the Microsoft.SharePoint.Linq namespace and it serves as the entry point for accessing or updating data in a SharePoint site. To use the DataContext class, you pass in the URL of the site whose data you would like to access, as in these examples:

DataContext dc = new DataContext("http://intranet/sites/finance");

DataContext dc = new DataContext(SPContext.Current.Web.Url);

You can then use your DataContext object to get a list object, by passing in the list’s entity type (which is defined in the class file you generated using SPMetal) as well as the name of the list. The list object that’s returned has a type of EntityList<T>, with the <T> being the type of objects in the list. In the example you just walked through, you specified in your Config.xml file that the class name (that is, the type) of an item in the Customers list is called Customer.

<ContentType Name="Item" Class="Customer" />

If you had a list called Customers in your site, you would retrieve the Customers list from the DataContext object like this:

EntityList<Customer> customers = dc.GetList<Customer>("Customers");

Once you have retrieved your EntityList<T> object, you can create a LINQ query using the list. LINQ queries are similar to T-SQL or CAML queries, in that they allow you to select items using keywords such as “select,” “from,” “orderby,” “group by,” and “where.” Unlike SQL queries, however, the select statement comes at the end of the statement. A very basic query, such as the one shown here, retrieves all the items from the Customers list:

var customerDataSource = from customer in customers

                         select customer;

The variable customers refers to the entity list that was retrieved from the DataContext object. SharePoint knows that the entity list is a collection of objects of a given type, so you can make up whatever variable name you would like to denote a single object from the entity list. In the previous snippet, the variable customer denotes a single object of type Customer in the customers entity list. The “select” statement simply returns the entire list item for each SharePoint list item in the list.

If you would like to return a particular field from the list you selected, and that field has been included in your entity class, you can use IntelliSense to reference it in your code. For example, to return just the Title column from the Customers list, you can execute the following LINQ query:

var customerDataSource = from customer in customers

                         select customer.Title;

In some cases, if you query a list that has a Lookup column to another list, IntelliSense will even allow you to select the column names from the joined list in your query. Say you have another list in your SharePoint site called Contacts, and this list has a column called First Name. If you created a Lookup column on your Customers list in SharePoint that pointed to the Contacts list (calling that Contact list content type PrimaryContact), you could actually return columns from both lists in a single LINQ query, like this:

var customerDataSource = from customer in customers

                         select new { customer.Title,

                             customer.PrimaryContact.FirstName };

Because you are selecting more than one field, it’s necessary to create a new array of selected fields by using the new keyword.

One of the many ways you might use data you retrieve from your LINQ query is to bind the results of your query directly to a web control that will be rendered in a browser, such as an ASP.NET GridView control. For an example of LINQ to SharePoint used in this way, see the following section of this chapter, “Example: Using LINQ to Query Lists.”

A custom DataContext object is created for you when you use SPMetal to create your entity classes, and you can use it to retrieve strongly typed list objects, instead of retrieving an entity list with a defined type. This presents an alternative way of executing the query you just walked through. When executing a query using your custom DataContext object, you can use an anonymous variable (for which you want to use the “using” keyword to prevent memory leaks) to create your object. If you called your entity class file Site.cs, your custom DataContext object would be called SiteDataContext. (If you’re not sure what your class name is, open up your automatically generated file and view the class name used at the very top, which inherits from Microsoft.SharePont.Linq.DataContext.) You would use your custom object like this:

using (var dc = new SiteDataContext(SPContext.Current.Web.Url))

{

}

You could then use your custom object directly to access the list you wanted to query, without needing to retrieve an entity list object first, as shown in this code snippet:

using (var dc = new SiteDataContext(SPContext.Current.Web.Url))

{

    var customerDataSource = from customer in dc.Customers

                             select customer;

}

The SiteDataContext object is already aware of the fact that there is a list called Customers.

image

Sometimes it’s possible to view the underlying CAML query that got executed when you executed a LINQ query. You can do this by accessing the DataContext.Log property. To read more about how to use this property, visit the online SDK article “How to: View CAML Generated by LINQ to SharePoint” at http://msdn.microsoft.com/en-us/library/ff798464.aspx.

Example: Using LINQ to Query Lists

This example walks you through the process of retrieving information from linked lists in SharePoint using LINQ to SharePoint. You will bind the results of your query to an ASP.NET GridView control, which you will deploy inside a Visual Web Part. You can download this project from Wrox.com. The project is titled Wrox.SixInOne.LINQWebPart.

Follow these steps:

1. Open the SharePoint site you used in the previous example. Create a new list called Contacts, based on the Custom List template.

2. Add a new Text column to the list, called First Name.

3. Add several new items to your new Contacts list.

4. Go to the Customers list you created in the previous example. Add a new Lookup column to the Contacts list called Primary Contact. Make the column required. In the Get Information From drop-down menu, select Contacts. In the In This Column drop-down menu, select Last Name. Click OK.

5. Add several new items to your Customers list.

6. Open up the Visual Studio project you created in the previous example.

7. Open up your Config.xml file. Add a new <List> node under the first one, which will reference the new Contacts list you created. Your new node should look like this:

<List Name="Contacts">

        <ContentType Name="Item" Class="Contact" />

</List>

8. Right-click your project and select Add ⇒ New Item from the context menu.

9. Select the Visual Web Part item template from the SharePoint 2010 node. Name your new Web Part Customers.

10. Open up the user control CustomersUserControl.ascx. Open up the Visual Studio Toolbox and drop a GridView control on the user control. Give the GridView control an ID of CustomerGridView.

11. Open up the code behind for the user control, CustomersUserControl.ascx.cs.

12. Add the following “using” statements to the top of the page:

using System.Linq;

using Microsoft.SharePoint;

using Microsoft.SharePoint.Linq;

13. In the Page_Load method, add the following code:

DataContext dc = new DataContext(SPContext.Current.Web.Url);

 

var customers = dc.GetList<Customer>("Customers");

 

var customerDataSource = from customer in customers

                         select new {CustomerName = customer.Title,

                             ContactLastName = customer.PrimaryContact.Title,

                             ContactFirstName = customer.PrimaryContact.FirstName};

 

CustomerGridView.DataSource = customerDataSource;

CustomerGridView.DataBind();

14. Go to the Build menu in Visual Studio and deploy your project. This should automatically compile your solution package and activate the feature in the SharePoint site to which you deployed your project.

15. Open up your site and add your new Web Part to a page. You should now see a grid that contains three columns of data (Figure 8-10).

AN INTRODUCTION TO WEB PARTS

This section will help you understand what a Web Part is and the difference between a Web Part server control and a Visual Web Part. It also walks you through the creation of a Web Part server control.

What Is a Web Part?

A Web Part is a self-contained unit of functionality that can be hosted on a web page and whose settings can be configured using a browser-based user interface called a tool pane. (Figure 8-11 shows the tool pane for the Tag Cloud Web Part.) The SharePoint Web Part framework is based on the ASP.NET Web Part framework; in fact, a Web Part is actually a compiled ASP.NET server control. As such, you can build a Web Part to have just about any kind of functionality you could build into a traditional web application.

Instances of a Web Part can be used again and again by being placed multiple times on a single page, or placed on multiple pages in a SharePoint site. Because Web Parts can be configured, it’s possible for the same Web Part to look very different when configured in different ways. Some Web Parts display information that is tied to some unique data source, so it’s possible for the same Web Part to return very different information, depending on which data source it has been connected to. For instance, a List View Web Part might show documents from your Shared Documents library or it might show files from your Style library, but the underlying Web Part was coded one time and simply configured two different ways.

A Web Part must be able to function completely by itself (that is, it should not be dependent on any other Web Part on the page for its functionality), but can interact with other Web Parts hosted on the same page, by either supplying or consuming data from those other Web Parts. For instance, you might have one List View Web Part that displays a list of the names of images from a picture library in your SharePoint site. If you placed that Web Part on a page, the Web Part would function properly, returning a list of pictures. However, it would be possible to place an Image Viewer Web Part on the same page, then instruct the List View Web Part to connect to the Image Viewer Web Part, so that each time a user selected an image from the picture library, that image would be rendered in the Image Viewer Web Part. When Web Parts work with each other on a page, they are considered connected. Figure 8-12 shows a List View Web Part that is displaying a list of documents in a library called Pictures. The Web Part is connected to an Image Viewer Web Part. When the user selects a picture from the Pictures list, a preview of the image shows up in the Image Viewer Web Part.

Although a Web Part can be configured so that every person who visits that Web Page sees the same configuration of the Web Part, it’s also possible for each user to configure a Web Part individually. And SharePoint can save the user’s personal configuration, so that on the next visit to the page hosting that Web Part, the user will see the version of the Web Part that he or she configured and not the configuration of the Web Part that was configured for all users. This is called Web Part personalization.

image

Personalization can reduce performance because SharePoint is required to save a separate version of a given Web Part for each individual who has personalized a Web Part. In addition, users who are not well trained in using Web Parts can accidentally delete or misconfigure Web Parts. For these reasons, it’s important to weigh the risks and benefits of enabling Web Part personalization for your end users. It’s possible to eliminate the ability for users to personalize Web Parts by modifying the underlying permission set(s) that users have been assigned to. To do so, unselect the Add/Remove Personal Web Parts and Update Personal Web Parts checkboxes on the Edit Permission Level administration page. You can also remove the ability for users to personalize a particular Web Part by unchecking the Allow Editing in Personal View checkbox in the Advanced section of a Web Part’s tool pane.

It’s possible to both close and delete a Web Part; if you close a Web Part, the Web Part is no longer displayed when you load the page, but the configuration of the Web Part is still stored with the page. You could add the Web Part back to the page at any time. In contrast, if you delete a Web Part, the Web Part is forever gone from the page.

image

If a Web Part is closed but not deleted, every time the page is retrieved, SharePoint must retrieve the information for that closed Web Part as well, reducing performance. If you believe you might need a Web Part added back to the page in the future, you might find it helpful to close the Web Part instead of deleting it, because the Web Part will still be available to you. However, if you don’t think you’ll need that Web Part again, it’s better to delete it than to close it, so that your page will render more quickly.

Building a Web Part

Building a new Web Part server control is easy with Visual Studio 2010. Once you have a SharePoint 2010 Visual Studio project, you can create a new Web Part by choosing the Web Part project item template. This will automatically add a new class to your project, which inherits from the class System.Web.UI.WebControls.WebParts.WebPart. When your class first gets created, it will look like this:

namespace Wrox.SixInOne.WebParts.WebPart1

{

    [ToolboxItemAttribute(false)]

    public class WebPart1 : WebPart

    {

        protected override void CreateChildControls()

        {

        }

    }

}

The first thing to note is the attribute ToolboxItemAttribute, which decorates your class. This attribute would be set to true if you were going to make this control available in the Visual Studio toolbox. However, because that’s not necessary for creating a SharePoint Web Part, this value can remain false.

A Web Part is essentially a container for other web controls. As such, you can create additional web controls and add them to the collection of controls in the Web Part you are building. You will do this in the CreateChildControls() method. For instance, if you wanted your Web Part to display the text “Hello, World!” you could add this code to your CreateChildControls() method:

Literal helloWorld = new Literal();

helloWorld.Text = "Hello, World!";

this.Controls.Add(helloWorld);

One of the things that makes Web Parts so versatile is the ability for end users to modify the properties of the Web Part in their browsers. It’s possible to create public properties on your class and then add attributes to those properties that will make those properties visible in the tool pane the user sees when configuring the Web Part. Table 8-3 shows some of the attributes you can use.

Table 8-3: Web Part Public Property Attributes

ATTRIBUTE DESCRIPTION EXAMPLE
WebBrowsable() A value of true indicates that this property will be editable in the Web Part tool pane. WebBrowsable(true)
WebDisplayName() The name of the field in the tool pane. WebDisplayName("Days")
WebDescription() The description of the field, which will be displayed as a tooltip when the user mouses over the name of the field in the tool pane. WebDescription("Number of days")
Personalizable() Indicates whether the property, when saved, will be available to every user who visits the page, or just the person who set the property. Possible values are PersonalizationScope.Shared or PersonalizationScope.User. Personalizable
(PersonalizationScope.Shared)

To allow a user to set a public property in your Web Part class, you could add the following code to that class:

private int days = 3;

 

[WebBrowsable(true),

WebDisplayName("Days"),

WebDescription("Number of days"),

Personalizable(PersonalizationScope.Shared)]

public int Days

{

    get { return days; }

    set { days = value; }

}

This property would generate a field called “Days” with a description of “Number of days” in the Web Part tool pane. Figure 8-13 shows what this would look like.

Deploying Your Web Part to SharePoint

Now that you have your underlying Web Part class written, you need to get the Web Part deployed to your site collection. Each Web Part must have a configuration file. This file will have an extension of .webpart. (In older versions of SharePoint, this file had a .dwp extension, which you might still find in legacy SharePoint applications.) The configuration file tells SharePoint basic information such as the name and the description of the Web Part. It also tells SharePoint that this Web Part references the underlying class that you just created. It does this by giving the assembly and type name of the Web Part class. The configuration file can also be used to set initial property values for the Web Part.

Listing 8-13 shows a sample configuration file. Notice that it is telling SharePoint that the name of the Web Part is “Web Part 1” and the description of the Web Part is “My Web Part.” It’s also telling SharePoint that this Web Part utilizes the type Wrox.SixInOne.WebParts.WebPart1.WebPart1 in the assembly Wrox.SixInOne.WebParts.

image Listing 8-13: WebPart1.webpart

<?xml version="1.0" encoding="utf-8"?>

<webParts>

  <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">

    <metaData>

      <type name="Wrox.SixInOne.WebParts.WebPart1.WebPart1,

        Wrox.SixInOne.WebParts.GetItems.GetItems,

        Wrox.SixInOne.WebParts, Version=1.0.0.0,

        Culture=neutral, PublicKeyToken=531c676be3760df2" />

          <importErrorMessage>Cannot be imported.</importErrorMessage>

    </metaData>

    <data>

      <properties>

        <property name="Title" type="string">Web Part 1</property>

        <property name="Description" type="string">My WebPart</property>

      </properties>

    </data>

  </webPart>

</webParts>

Once the configuration file is in place, you need to create a Module element to deploy this configuration file to your site collection Web Part gallery. The URL of the Web Part gallery in any site collection is /_catalogs/wp. Your Module element might look like this:

<?xml version="1.0" encoding="utf-8"?>

<Elements xmlns="http://schemas.microsoft.com/sharepoint/" >

  <Module Name="WebPart1" Url="_catalogs/wp">

    <File Path="WebPart1WebPart1.webpart" Url="WebPart1.webpart"

Type="GhostableInLibrary">

      <Property Name="Group" Value="My Custom Web Parts" />

    </File>

  </Module>

</Elements>

This is telling SharePoint that you want to deploy your Web Part configuration file to the Web Part gallery, and your Web Part should be placed into a group called “My Custom Web Parts.” The group name becomes important when users want to add Web Parts to their pages. Web Parts with the same group name will appear in a folder under the Categories heading when a user wants to add a Web Part to a page. Figure 8-14 shows that the Web Parts Content Editor, Image Viewer, and so on all fall under the group name Media and Content.

Luckily, when you create a new Web Part using the Visual Studio project item template, Visual Studio will automatically generate your Web Part configuration file and your Module element for you. However, it’s important to know how to manually configure each of the files because you will most likely want to be able to configure the Web Part’s name, description, and group before you deploy it to your server. Figure 8-15 shows you the three files that Visual Studio creates for you when you create a Web Part using the Web Part project item template: the class (.cs) file that defines the Web Part; the Web Part configuration (.webpart) file; and the element manifest (.xml) that deploys it to the Web Part gallery.

Example: Building the GetItems Web Part

Follow these steps (you can find all the code snippets in the example on the Wrox website, identified as Wrox.SixInOne.WebParts):

1. Create a new Project in Visual Studio 2010.

2. Select the Visual C# Empty SharePoint Project template. Give your project a name of Wrox.SixInOne.WebParts and click the OK button.

3. On the SharePoint Customization Wizard dialog box, enter the URL of the website you would like to use for debugging, select the Deploy as a Farm Solution radio button, and click the Finish button.

4. Right-click your project and select Add ⇒ New Item from the Context menu. Select the Visual C# ⇒ SharePoint ⇒ 2010 node from the Installed Templates pane, then select the Web Part item template. Give your Web Part a name of GetItems.

5. Open the GetItems.cs file.

6. Add the following “using” statements to the top of the page:

using System;

using System.ComponentModel;

using System.Data;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using Microsoft.SharePoint;

7. Add the following two private variables:

private string listName = "Documents";

private int days = 3;

8. Add the following two public properties, with their accompanying attributes:

[WebBrowsable(true),

WebDescription("Select the name of the list you would like to query."),

WebDisplayName("List Name"),

Personalizable(PersonalizationScope.Shared)]

public string ListName

{

    get { return listName; }

    set { listName = value; }

}

 

[WebBrowsable(true),

WebDescription("Number of days"),

WebDisplayName("Days"),

Personalizable(PersonalizationScope.Shared)]

public int Days

{

    get { return days; }

    set { days = value; }

}

9. Add the following code to the CreateChildControls() method:

SPWeb web = SPContext.Current.Web;

SPList list = null;

try

{

    //Get the list specified in the tool pane

    list = web.Lists[listName];

}

catch

{

    Literal errorMessage = new Literal();

    errorMessage.Text =

        String.Format("A list with name {0} does not exist in this site.",

        listName);

    this.Controls.Add(errorMessage);

}

 

//Get all the items which are newer than the number of days old

//specified in the tool pane

string camlQueryText =

    String.Format("<Where><Geq><FieldRef Name='Modified' />" +

    "<Value Type='DateTime'>" +

    "<Today OffsetDays='-{0}' /></Value></Geq></Where>",

    days);

SPQuery camlQuery = new SPQuery();

camlQuery.Query = camlQueryText;

SPListItemCollection items = list.GetItems(camlQuery);

DataTable data = items.GetDataTable();

 

//If the query returns list items, bind them to a DataGrid control

if (data != null)

{

    DataGrid dg = new DataGrid();

    dg.AutoGenerateColumns = false;

 

    BoundColumn bc = new BoundColumn();

    bc.HeaderText = "Title";

    bc.DataField = "Title";

    dg.Columns.Add(bc);

 

    BoundColumn bc2 = new BoundColumn();

    bc2.HeaderText = "Modified";

    bc2.DataField = "Modified";

    dg.Columns.Add(bc2);

 

    this.Controls.Add(dg);

    dg.DataSource = data;

    dg.DataBind();

}

else

{

    Literal errorMessage = new Literal();

    errorMessage.Text = "No items returned.";

    this.Controls.Add(errorMessage);

}

10. Press the F5 button to debug your solution.

11. Browse to your site and add your new Web Part to a page.

12. Modify the Web Part to open up the tool pane. Expand the section titled Miscellaneous. You should see two fields that reflect the List Name and Days public properties on your class (as shown in Figure 8-13). Modify the properties and see if your Web Part returns additional or fewer items.

Understanding Visual Web Parts

It can be difficult to build Web Part server controls because you are programmatically adding child web controls, and you lack any sort of visual indication of what your Web Part will look like when rendered on a web page. An alternative to building your Web Parts using server controls is to create something called a Visual Web Part. A Visual Web Part is essentially a Web Part that contains a single child control, and this child control is an ASP.NET user control. By leveraging a user control, you have the ability to use the Visual Studio designer to drag and drop other controls onto your design surface. This provides a much more user-friendly way of designing Web Parts.

To create a Visual Web Part, simply add a new Visual Web Part to your existing Visual Studio SharePoint 2010 project using the Visual Web Part project item template. This will automatically create the Web Part class, configuration file, and Module element, but it will also create the user control and user control code-behind page, which you can use to design your Web Part.

image

User controls must be deployed to the ~/14/TEMPLATE/CONTROLTEMPLATES folder on the filesystem to be considered “safe” by SharePoint. This is a location shared among all web applications in the server farm. As such, it is not possible to deploy Visual Web Parts in a sandboxed solution. However, Microsoft has released a Visual Studio add-on called Visual Studio 2010 SharePoint Power Tools, which allows you to deploy Visual Web Parts in a sandboxed solution. To download the add-on, visit http://visualstudiogallery.msdn.microsoft.com/en-us/8e602a8c-6714-4549-9e95-f3700344b0d9.

To build your own Visual Web Part, follow the steps laid out in the section “Example: Creating a Feature and Solution Using Visual Studio 2010” in Chapter 7.

AN INTRODUCTION TO THE REST INTERFACE

Up until now, you’ve learned about how to access SharePoint information using code that executes on the SharePoint server itself. The rest of this chapter focuses on methods of accessing SharePoint data from remote locations, whether that means accessing SharePoint using HTTP, a client application such as Silverlight, or web service.

SharePoint 2010 provides a way of accessing list data using a Representational State Transfer (REST) interface. (Interfaces utilizing the REST interface are referred to as RESTful.) The RESTful interface provides a simple way for external applications to access and modify SharePoint data. It makes it possible to retrieve an XML representation of list data by simply supplying a URL. It’s possible to carry out basic CRUD (create, read, update, and delete) operations using the REST interface, by using the HTTP operations of POST, GET, PUT, and DELETE. By passing in various parameters with your URLs, it’s possible to filter and sort the data that you get back.

image

For your RESTful service to work, you must have installed the ADO.NET Data Services Update for .NET Framework 3.5 SP1.

image

You can supply ADO.NET Data Services with a SharePoint data source by passing it a RESTful URL from SharePoint. To learn more about this, read the article “Query SharePoint Foundation with ADO.NET Data Services” at http://msdn.microsoft.com/en-us/library/ee535480.aspx.

To access the RESTful service, you’ll need to reference the following URL: /_vti_bin/ListData.svc.

This URL should be appended to whichever site you are querying. For instance, if you want to retrieve list data from the accounting department’s site in your intranet, you would use a URL of http://intranet/sites/accounting/_vti_bin/ListData.svc.

image

You can see what kinds of results get returned by browsing to the list data service URL of the SharePoint site in your browser. If the results conform to an RSS standard, your browser may interpret the results as an RSS feed for you to subscribe to, which means it won’t display straight XML. When querying a particular list item, your browser might not be able to interpret the results as a feed and might give you an error.

You can get every list item out of a particular list in the site by appending the name of the list to the end of the URL. For example, to get all the documents from the Documents library in your accounting site, you would use a URL of:

http://intranet/sites/accounting/_vti_bin/ListData.svc/Documents

You can retrieve one particular list item from a list by passing in the ID of that list item after the list name, like this:

http://intranet/sites/accounting/_vti_bin/ListData.svc/Documents(2)

You can also find out the column value for one particular list item by passing the column name after the item number like this:

http://intranet/sites/accounting/_vti_bin/ListData.svc/Documents(2)/Title

You can sort your results by using the parameter $orderby and passing in the name of the column you would like to sort your results by. For instance, if you want to sort your documents by title, you would use a URL of:

http://intranet/sites/accounting/_vti_bin/ListData.svc/Documents?$orderby=Title

You can even execute a CAML query on your list by passing in a $filter parameter. For instance, if you wanted to retrieve a document whose title was My Document, you could use a URL like:

http://intranet/sites/accounting/_vti_bin/ListData.svc/Documents?$filter=Title eq 'My Document'

Although it’s unconventional to have spaces in your URL, it’s permitted when using the RESTful service.

To read more about the RESTful service, visit the SDK article online:

http://msdn.microsoft.com/en-us/library/ff521587.aspx

AN INTRODUCTION TO THE CLIENT OBJECT MODEL

At times it becomes helpful to contact your SharePoint server from a client application, whether that client is a Windows or web application. You might want to be able to retrieve and update information from SharePoint from a Web Part that lives on one of your SharePoint pages, without needing to post the entire page back to the server, causing the whole page to reload. That Web Part could be created using managed .NET code, or it could even be a Silverlight control. SharePoint 2010 provides developers with a new tool that makes it easy to contact the SharePoint server in a lightweight manner. It’s called the client-side object model (CSOM).

The CSOM includes a set of objects that mirrors the server-side object model, allowing you to interact with objects such as site collections, sites, lists, and list items. However, the object model itself is pared down, providing a subset of functionality that focuses on the most heavily used operations. This means that smaller class libraries need to be downloaded to the client when the CSOM is being utilized by Silverlight or ECMAScript.

image

ECMAScript is a scripting language standard that adheres to a specification set out by an organization called Ecma International. In SharePoint, ECMAScript is usually written using JavaScript or Jscript.

SharePoint 2010 contains three subsets of the client object model:

  • Client-side API for code running in a .NET 3.5 or higher managed code application
  • Silverlight API
  • ECMAScript API

The client object models are implemented as a Windows Communication Foundation (WCF) service. Each of these object models uses a proxy that serializes commands and sends them asynchronously as XML to the server in a single HTTP request. When the request reaches the server, the server-side object model takes over to access the server. A response is sent back to the client using JavaScript Object Notation (JSON). The proxy then takes that information and associates it with the appropriate client objects.

Understanding the Client Objects

The .NET and Silverlight client object models use the namespace Microsoft.SharePoint.Client, whereas the ECMAScript client object model uses a namespace of SP. For instance, to reference the WebParts class using the .NET or Silverlight client object models, you would use the namespace Microsoft.SharePoint.Client.WebParts, whereas using ECMAScript you would use SP.WebParts. The names of the objects are similar to the server object names, but with the absence of the “SP” prefix. For instance, using the server-side object model, to access a site collection you would reference an SPSite object. Using the client object model, you would reference a Site object. In the same way, using the CSOM you would access sites, lists, and list items using Web, List, and ListItem objects.

One of the advantages of the CSOM is that queries and updates to the server are performed in batches, reducing network traffic. That means you can create a set of commands that tell SharePoint what you want to do, then send them off to the server all at once, rather than having to send multiple commands to the server one at a time, consuming network resources. SharePoint does this by keeping track of an object on the client side, both before and after the object has been retrieved from the server.

The first step to utilizing the CSOM is to create an object that will manage the connection with the server. This object is the ClientContext object. This object is used to retrieve information from the server that can be assigned to a client object. As when creating a new SPContext object using the server object model, using the CSOM you must also create a new context object by passing in the URL of the SharePoint site you wish to use. For instance, Listing 8-14 creates a new context object that is connected to the marketing department’s site on the intranet. (This and the following three listings can be downloaded from Wrox.com, where they are identified as part of the CSOM.cs code block.) The listing creates a Web object, but that object will remain empty unless a request is made to the server to populate it with information about the site. This can be done using the Load() method of the ClientContext object. Although the code is telling SharePoint that it wants information about the marketing site assigned to the marketingSubsite object, the object will not actually be populated until the ExecuteQuery() method of the context object is executed, at which point a call to the server is made. When the proxy class receives a response from the server, it will populate the marketingSubsite object with property information, such as the name of the site.

image Listing 8-14: Creating the ClientContext (CSOM.cs)

ClientContext ctxt = new ClientContext("http://intranet/sites/marketing");

Web marketingSubsite = ctxt.Web;

ctxt.Load(marketingSubsite );

ctxt.ExecuteQuery();

string siteTitle = marketingSubsite.Title;

Keep in mind, however, that only the specific information requested will be populated when the query is executed. For instance, trying to access the collection lists that are inside the marketing site will return an error unless that collection has been retrieved from the server as well, as in Listing 8-15.

LISTING 8-15: Getting List Data (CSOM.cs)

ListCollection lists = marketingSubsite.Lists;

ctxt.Load(lists);

ctxt.ExecuteQuery();

To update objects, you must call the Update() method on them, much as you would when using the server object model. However, as with retrieving information, the update will not actually take place on the server until the ExecuteQuery() method is executed. For instance, Listing 8-16 demonstrates how you can update the title of a site.

LISTING 8-16: Updating Site Data (CSOM.cs)

marketingSubsite.Title = "New Title";

marketingSubsite.Update();

ctxt.ExecuteQuery();

string newTitle = marketingSubsite.Title;

To delete an object, you’ll call the DeleteObject() method on the object. (This is slightly different than the server object model, where you use a method simply called Delete().) As always, you must call the ExecuteQuery() method to communicate the deletion to the server.

Because code is executing on the client side, the less code that can be retrieved from the server the better, because it means less processing on the client. It’s possible to use complex queries, LINQ, and Lambda expressions to create detailed queries that return just the information needed to perform an action. To learn more about these kinds of queries, read the Managed Client Object Model SDK documentation online at http://msdn.microsoft.com/en-us/library/ee537247.aspx.

Example: Creating a CSOM Console Application

This section walks you through the steps of creating a console application that uses the client-side .NET managed object model to access your SharePoint site. (You can find all the code snippets in the example on the Wrox website, identified as Wrox.SixInOne.CSOM.)

1. Create a new project in Visual Studio 2010 using the C# Console Application project template. Make sure your project is targeted to the .NET Framework 3.5 or the .NET Framework 4.5. Give your project a name of Wrox.SixInOne.CSOM.

2. Add a reference to the following two assemblies, located in the ISAPI folder in your SharePoint root folder: Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll. (Verify that your project is still targeted to the framework you initially set when creating the project. Visual Studio might reset your project to be targeted to the .NET Framework 3.5 or 4.0 Client Profile once you include the client assemblies. If it has changed your targeted framework, you will need to change it back to your initial setting.)

3. Copy the code from Listings 8-14, 8-15, and 8-16, and place it inside the Main() method of your Program class, replacing the URL of the site with the URL of a site on your development SharePoint server.

4. Add the following line of code after the code you just added:

Console.WriteLine(lists.Count.ToString());

Console.WriteLine(newTitle);

5. Run your project. You should see the console write out the number of lists that are in your site. You should also notice that the name of your site has now changed to “New Title.”

AN INTRODUCTION TO THE SHAREPOINT WEB SERVICES

You have now learned how to access SharePoint using the server object model, LINQ to SharePoint, REST, and the client object model. Another way to access your SharePoint server is to use the traditional ASP.NET web services that are included as part of your SharePoint server.

Separate web services exist for interacting with objects on the server such as site collections, sites, lists, permissions, people, alerts, meetings, and versions of list items. You can find each of these web services in the _vti_bin virtual directory of your IIS web application.

image

In the last version of SharePoint, SharePoint web services were the primary means of communicating with SharePoint from an external location. However, with the advent of the CSOM in SharePoint 2010, Microsoft is encouraging developers to utilize the CSOM technologies instead of the SharePoint web services. As a result, not all web service methods may function as smoothly in SharePoint 2010 as they did in SharePoint 2007.

When accessing information from SharePoint using a web service, it’s important to let SharePoint know where you want to get information from. To add a reference to the web service in your Visual Studio project, pass in the URL of the site you want to access, followed by the web service URL in the _vti_bin folder. For example, if you wanted to access list information in the marketing site, you would add a web reference in Visual Studio to the Lists.asmx web service using this URL:

http://intranet/sites/marketing/_vti_bin/Lists.asmx

One of the first things you will need to establish when connecting to the server is the credentials with which you are authenticated against SharePoint. Just as when logging into SharePoint via a web browser, a web service will be able to retrieve or update only those objects on the server that a user has permission to access or update. To connect to your SharePoint server using the credentials of the user running the web service, set the Credentials property of the object you created from the web service to System.Net.CredentialCache.DefaultCredentials, like this:

Web_Reference.Lists listsService = new Web_Reference.Lists();

listsService.Credentials = System.Net.CredentialCache.DefaultCredentials;

Each web method for each web service is different. In some cases you will need to create XML nodes that contain information you want to send with your web service request. For instance, if you want to retrieve list items from a given list, you can tell SharePoint information about the items you want to get back from your call to the web service. You can do this by telling SharePoint to execute a specific CAML query, or telling SharePoint to retrieve items from a particular view set up on the list. You can also tell SharePoint to retrieve items from a particular folder within the list.

The following code snippet creates a new XML node called Query, which contains a CAML query telling SharePoint to retrieve the items where the column Season has a value of Spring:

XmlDocument xmlDoc = new XmlDocument();

XmlElement query = xmlDoc.CreateElement("Query");

query.InnerXml = "<Where><Eq><FieldRef Name="Season"/>" +

    "<Value Type="Text">Spring</Value></Eq></Where>";

When retrieving items from a particular list, you will also create a node called ViewFields, which tells SharePoint which fields you want returned with your query and in which order, as well as a QueryOptions node, which will tell SharePoint information such as whether the items retrieved should come from a particular folder in the list.

The following snippet shows an example of a web service call that returns the title of all the items from a list called Products in the intranet site where the Season column is set to Spring.

//SharePointWebService is the namespace of the Web Service that was registered

//with the project.

SharePointWebService.Lists listSvc = new SharePointWebService.Lists();

 

listSvc.Credentials = System.Net.CredentialCache.DefaultCredentials;

 

listSvc.Url = "http://intranet/_vti_bin/Lists.asmx";

 

System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();

System.Xml.XmlElement query = xmlDoc.CreateElement("Query");

System.Xml.XmlElement viewFields = xmlDoc.CreateElement("ViewFields");

System.Xml.XmlElement queryOptions = xmlDoc.CreateElement("QueryOptions");

 

XmlElement query = xmlDoc.CreateElement("Query");

query.InnerXml = "<Where><Eq><FieldRef Name="Season"/>" +

    "<Value Type="Text">Spring</Value></Eq></Where>";

viewFields.InnerXml = "<FieldRef Name="Title" />";

queryOptions.InnerXml = "";

 

System.Xml.XmlNode nodeListItems =

    listSvc.GetListItems("Products", null, query, viewFields, null, queryOptions,

null);

SUMMARY

In this chapter you learned about various ways to interact with SharePoint programmatically and declaratively. You learned how to work with site collections, sites, lists, list items, and folders, using the server-side object model. You learned how to provision new items in SharePoint using CAML and how to use CAML to retrieve information from the SharePoint database. This chapter showed you how you can use LINQ to SharePoint to access SharePoint content in a strongly typed and readable way. This chapter gave you an introduction to what a Web Part is, and how to build a Web Part server control as well as a Visual Web Part. You learned about accessing information in SharePoint using the REST model and the client object model. Finally, you learned about the availability of the SharePoint web services.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset