Saturday, December 15, 2012

7 rules and best practices for using the SharePoint .NET client object model

Last week I found good explanatory article related to using good practices in CSOM  from MSDN. I am sharing here for your help. Slightly modified for simplification  :)

1.Call ClientContext.ExecuteQuery before accessing any value properties

The SharePoint .NET Framework CSOM requires that you use a SQL-like programming pattern: declare what you want and execute the query before you access the data. For example, the following code, attempts to display the SharePoint website's title, will throw an exception.
C#
// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");

Web web = context.Web;
label1.Text = web.Title; 

This code fails because SharePoint .NET Framework CSOM code must:
Build either an ad hoc SQL query or a stored procedure.
Execute the SQL query.
Read results from SQL.
In SharePoint .NET Framework CSOM , when you call a method you build a query. Queries accumulate and are not sent to the server until ExecuteQuery is called. The following example shows the code that is required to display the website's title. You will also need to add a using statement for System.Linq. Also, add an alias to the using statement for theMicrosoft.SharePoint.Client namespace so you can refer to its classes unambiguously. For example, using SP = Microsoft.SharePoint.Client;.
C#
// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");

Web web = context.Web;

context.Load(web, w => w.Title);

context.ExecuteQuery();

label1.Text = web.Title;  

The differences are the addition of these lines:
context.Load(web, w => w.Title);
context.ExecuteQuery();

The first line creates a query for the web's Title property. The second line executes the query.

2. Do not use value objects returned from methods or properties in the same query

When a value object is returned from a method or property, you cannot use that object until after you have executed the query. For example, the following code tries to create a SharePoint list that has the same title as the parent website, but it will throw an exception.
C#
// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");

Web web = context.Web;
ListCreationInformation creationInfo = new ListCreationInformation();
creationInfo.Description = web.Title;
creationInfo.Title = web.Title;
List newList = web.Lists.Add(creationInfo); 

An exception is thrown because the property is not available before you execute the query. In SQL, you would declare a local variable to hold the value for web.Title and use the local variable for web creation. In the client library, you can’t create a local variable. You have to split functionality into two separate queries as is shown in the following example. You will also need to add a using statement for System.Linq. Also, add an alias to the using statement for Microsoft.SharePoint.Client namespace so you can refer to its classes unambiguously. For example, using SP = Microsoft.SharePoint.Client;.
C#
// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");

Web web = context.Web;

context.Load(web, w => w.Title);

context.ExecuteQuery();

ListCreationInformation creationInfo = new ListCreationInformation();
creationInfo.Description = web.Description;
creationInfo.Title = web.Title;
SP.List newList = web.Lists.Add(creationInfo);

context.ExecuteQuery(); 

The difference is the following three lines:
C#
context.Load(web, w => w.Title);
context.ExecuteQuery();
...
context.ExecuteQuery();

3.Using methods or properties that return client objects in another method call in the same query

Unlike a value object, a client object can be used in another method call in the same query.
In .NET remoting, the value object is a class or struct that is marshaled by value, while the client object is a class or struct that is marshaled by reference. For example, the ListItem is a client object, while the UrlFieldValue and other field values are value objects.
In the client library, the corresponding server object has the [ClientCallable(ValueObject = true)] attribute. Those values could have only properties and no methods. Primitive types, such as strings and ints, are treated as value objects. All the values are marshaled between the client and the server. The default value of the ValueObject is false.
The counterpart to the value object is the client object. If the corresponding server object has the [ClientCallable(ValueObject = false)] attribute, the object is a client object. For client objects, we keep track of how the object is created; this is called ObjectPath in the client library implementation. For example, if we have code like the following:
C#
ClientContext context = new ClientContext("http://SiteUrl");
Web web = context.Web;
SP.List list = web.Lists.GetByTitle("Announcements");

We know that the list is created by:
·         Getting the Web property from the context.
·         Getting the Lists property from the above result.
·         Invoking the GetByTitle method with the Announcements parameter from the above result.
When the SharePoint .NET Framework CSOM passes this information to the server, you can recreate the object on the server. In the client library, you can keep track of the ObjectPath that the client object is created. Because you know how the object is created, you could use the object as a parameter to invoke other methods within the same query.

4.Group data retrieval on the same object together to improve performance

When reading multiple pieces of data from the same object, you should try to get all of it in a single query; that is, a single call to the Load(T, []) method. The following code shows two ways to retrieve a website's title and description and the Announcements list's description. To compile this code, you need to add a using statement for System.Linq. Also, add an alias to the using statement for the Microsoft.SharePoint.Client namespace so you can refer to its classes unambiguously. For example, using SP = Microsoft.SharePoint.Client;.
C#

static void Method1()
{
    ClientContext context = new ClientContext("http://SiteUrl");
    Web web = context.Web;
    SP.List list = web.Lists.GetByTitle("Announcements");
    context.Load(web, w => w.Title, w => w.Description);
    context.Load(list, l => l.Description);
    context.ExecuteQuery();
}

static void Method2()
{
    ClientContext context = new ClientContext("http://SiteUrl");
    Web web = context.Web;
    SP.List list = web.Lists.GetByTitle("Announcements");
    context.Load(web, w => w.Title);
    context.Load(list, l => l.Description);
    context.Load(web, w => w.Description);
    context.ExecuteQuery(); 
}

These are not equally efficient. In Method1, the code to retrieve the web's title and description is grouped together. In Method2, the code to retrieve the web's title and description is separated by other actions. This means that Method2 will trigger two separated queries on the same web object, and there will be two result sets for the same web. Because the client library tries to return consistent data, the second result set will include both the title and description. You could think of the previous code as the following.
SQL
Method1:
SELECT Title, Description FROM Webs WHERE ...
SELECT Description FROM Lists WHERE …

Method2:
SELECT Title FROM Webs WHERE …
SELECT Description FROM Lists WHERE …
SELECT Title, Description FROM Webs WHERE …

5. Specify which properties of objects you want to return

In the SharePoint server object model, if you get an SPWeb object, you can inspect all of its properties. In SQL, to get all of the columns of a table you can run:
SQL
SELECT * FROM Webs
In the client library, neither Load nor any other method returns all properties, so you have to explicitly specify what you want. For example, the following code retrieves the website object without specifying which properties to return. It then tries to read two properties and one of them is not among the properties that is automatically returned by Load. This code throws an exception.
C#
// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");

Web web = context.Web;
context.Load(web);
context.ExecuteQuery();

Console.WriteLine(web.Title);
Console.WriteLine(web.HasUniqueRoleAssignments); 

To get the code to compile successfully, update it to the following. To compile this code, you need to add a using statement for System.Linq. Also, add an alias to the using statement for the Microsoft.SharePoint.Client namespace so you can refer to its classes unambiguously. For example, using SP = Microsoft.SharePoint.Client;.
C#
// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");

Web web = context.Web;
context.Load(web);
context.Load(web, web => web.HasUniqueRoleAssignments);
context.ExecuteQuery();

Console.WriteLine(web.Title);
Console.WriteLine(web.HasUniqueRoleAssignments); 

6.Use conditional scope to test for preconditions before loading data

To conditionally execute code, set a conditional scope by using a ConditionalScope object. For example, retrieve the list property when the list is not null. You will also need to add usingstatements for System.Collections.Generic and System.Linq. Also, add an alias to the using statement for the Microsoft.SharePoint.Client namespace so you can refer to its classes unambiguously. For example, using SP = Microsoft.SharePoint.Client;.
C#
// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");

SP.List list = context.Web.GetCatalog(ListTemplateType.WebPartCatalog);
BasePermissions perm = new BasePermissions();
perm.Set(PermissionKind.ManageLists);

ConditionalScope scope =
    new ConditionalScope(context,
                         () => list.ServerObjectIsNull && context.Web.DoesUserHavePermissions(perm).Value);
using (scope.StartScope())
{
    context.Load(list, l => l.Title);
}
context.ExecuteQuery();

label1.Text = scope.TestResult.Value;

if (scope.TestResult.Value)
{
    label1.Text = list.Title;
} 

7. Use an exception handling scope to catch exceptions

This example shows how to create and use an exception handling scope with an ExceptionHandlingScope object. The scenario is to update the description of a list and also enable folder creation. There is a possibility that the list might not exist.
C#


// Starting with ClientContext, the constructor requires a URL to the
// server running SharePoint.
ClientContext context = new ClientContext("http://SiteUrl");

ExceptionHandlingScope scope = new ExceptionHandlingScope(context);

using (scope.StartScope())
{
    using (scope.StartTry())
    {
        List fooList = context.Web.Lists.GetByTitle("Sample");
        fooList.Description = "In Try Block";
        fooList.Update();
    }
    using (scope.StartCatch())
    {
        // Assume that if there's an exception,
        // it can be only because there was no "Sample" list.
        ListCreationInformation listCreateInfo = new ListCreationInformation();
        listCreateInfo.Title = "Sample";
        listCreateInfo.Description = "In Catch Block";
        listCreateInfo.TemplateType = (int)ListTemplateType.Announcements;
        List fooList = context.Web.Lists.Add(listCreateInfo);
    }
    using (scope.StartFinally())
    {
        List fooList = context.Web.Lists.GetByTitle("Sample");
        fooList.EnableFolderCreation = true;
        fooList.Update();
    }
}

context.ExecuteQuery(); 



 

2 comments:

Sean O'Leary said...

Thanks for sharing this information. There are many processes within SharePoint that can be confusing if you've never done it before. This is a helpful outline.

Pavankumar.Ch said...

Thank you so much, Really needful information