Best Practices SharePoint Client object Model

As full trust code is deprecated in SharePoint 2013, and Microsoft is pushing SharePoint Online and the App model and here we are bound to use Client object model so, many developers are finding that they must move from the server side object model to the client side object model (CSOM) With this, there are a number of challenges: many features are missing in the client object model, and many tasks that were once easy are now difficult. Hre are some points that we must consider when we work with CSOM

Best Practice 1. We should only request what we want [ and here again we must request everything we want in one go]

Example: We must explicitly request every property of every object we want. This is really a fundamental basic of CSOM programming – after all, it’s designed to be used across a network. If you only need a user’s Title and LoginName, then only request those:

 var objUser = clientContext.Web.CurrentUser;

clientContext.Load(obUser, user => user.Title, user => user.LoginName);

 later on in our code if we need to send the user an email, then add their email address to the earlier request. Don’t go back to the server twice! The cost of always requesting an additional property is miniscule in comparison to going all the way to the server and back an extra time later on.

Best Practice 2. Run ClientContext.Executequery or ClientContext.ExecuteQueryAsync before accessing any item properties.

Example: a. var clientContext = SP.ClientContext.get_current();

b. var web = context.get_web();

c. var currentUser = web.get_currentUser();

d. groupCollection = currentUser.get_groups();

e. context.load(currentUser);

f. context.load(groupCollection,

 ‘Include(Title,Id,Users.Include(Id,Title,LoginName))’);

g. context.executeQueryAsync(function () {

h.    var termArray = [];

i.    var groupEnumerator = groupCollection.getEnumerator();

j.    while (groupEnumerator.moveNext()) {

k.       var oGroup = groupEnumerator.get_current();

l.       var groupName = oGroup.get_title();

m.       termArray.push({ groupName: groupName });

n.       }

o.    }

p.    , function (s, a) {

q.       Console.log(a.get_message());

r. });

This example is based on the JavaScript object model. Line ‘a’ gets the current SharePoint context, Line ‘b’ gets the web object, ‘c’ gets the current logged in user, and ‘d’ gets all the groups current user belongs to. If you notice, all four statements are dependent on each other.

Until Line ‘d’, none of the objects are retrieved yet. The next two statements are instructions to load the objects and statement. In Line ‘g’, ‘executeQueryAsync’ is actually responsible for retrieving the objects and the properties.

For example:Note: This is a client side code in C#.

a. ClientContext context = new ClientContext(“http://siteCollUrl”);

b. Web web = context.Web;

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

d. context.ExecuteQuery();

e. GroupCreationInformation grpCreationinfo =

 new GroupCreationInformation();

f. grpCreationinfo.Title = web.Title + “_NewGroup”;

g. grpCreationinfo.Description = “New group creation”;

h. web.SiteGroups.Add(grpCreationinfo);

i. context.ExecuteQuery();

In the above example, the web’s title is used to create a group. Because the web’s title is of type value, we need to use ExecuteQuery to retrieve the title first and then create the group.

Objects like Web, ClientContext are passed by reference which is they are not copied. So there is no need to worry about performance. However pasing ClientContext object as a parameter between methods make more sense as we can then load whatever objects we want from it inside the method.

Best Practice 3. Use an exception handling scope to catch exceptions

Example: The below 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.
// 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();  

Best Practice 4. We must call Call ExecuteQuery Sparingly

Example: Again, an obvious one. But there are scenarios where you will be calling ExecuteQuery where you don’t need to. In case you didn’t know, ExecuteQuery is the method that causes all of your requests to go to the server in a single batch, so it’s slow!

In the below example a look at this scenario. If we are creating a list, we might think we need to write code as follows:

List list = web.Lists.Add(…);

ctx.ExecuteQuery(); //Create the list

ctx.Load(list, l => l.DefaultViewUrl); // Request the new list’s URL

ctx.ExecuteQuery(); // Get the new list’s DefaultViewUrl

In fact, you don’t need that first ExecuteQuery. It’s not intuitive, but you can create the list, get its URL, and submit both requests in one go:

This is an example of the type of creative way you can help prevent performance issues by minimizing the number of times you call ExecuteQuery.

Best Practice 5. Caching Data in the Session

Example: If every page in our app is requesting the same data from SharePoint, then we can store it temporarily in the user session cache. This will prevent having to make a round trip to the SharePoint server on every page request. Additionally, since it’s in the user session cache, it is scoped to each user individually. If we want to cache application-wide data, you can store it in the Application cache. See this MSDN article for more information.

So, let’s assume a scenario where each page checks whether or not the user is allowed access to it based on whether they are a site administrator:

public bool CheckPrivileges()

{

    var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);

    using (var clientContext = spContext.CreateUserClientContextForSPHost())

    {

        var currentUser = clientContext.Web.CurrentUser;

        clientContext.Load(currentUser, u => u.IsSiteAdmin);

        clientContext.ExecuteQuery();

        return currentUser.IsSiteAdmin;

    }

}

We can simply wrap that CheckPrivileges method in another that performs session caching:

Hide   Copy Code

public bool CheckPrivilegesWithSessionCaching(HttpContextBase httpContext)

{

    string key = “IsSiteAdmin”;

    var keys = httpContext.Session.Keys.Cast<string>().ToList();

    if(keys.Contains(key))

    {

        return (bool)httpContext.Session[key];

    }

    else

    {

        bool result = CheckPrivileges(httpContext);

        httpContext.Session[key] = result;

        return result;

    }

}

Note that if we are storing large amounts of data, this solution won’t scale well (since it is ‘In Memory’ caching) – we could store cached data in our database instead.

Additionally, we cannot assume that data will be available in the session cache – it could be cleared by ASP.NET at any time, or, due to load balancing it could be cached differently on separate servers. As long as you go back to SharePoint to retrieve it when necessary, this shouldn’t be a problem.

Best Practice 6. Use CAML Query to Filter Retrieval of List Items

Example: If we are retrieving some items from a list, it is tempting to retrieve all items and then filter it in our code. However, we can use CAML queries to perform the filtering server side. It can be a little bit awkward (coding in XML) but it’s worth getting right for the potential speed increases we will see, especially for large lists.

For example, this is the lazy way to get list items:

CamlQuery query = CamlQuery.CreateAllItemsQuery();

var items = list.GetItems(query);

And here’s a formatted CAML query with a where clause:

CamlQuery query = new CamlQuery()

{

    ViewXml = string.Format(“<View><Query><Where><Eq><FieldRef Name='{0}’ /><Value Type=’String’>{1}</Value></Eq></Where></Query></View>”,

                        “FirstName”, “Eric”)

};

 var items = list.GetItems(query);

Note the ‘View’ outer tag which is required when querying with CSOM, unlike the server object model version.

Here’s another trick – we can actually get all folders, subfolders and/or files from a document library, in a single query, by specifying RecursiveAll:

CamlQuery allFoldersCamlQuery = new CamlQuery()

{

    ViewXml = “<View Scope=’RecursiveAll’>”

                        + “<Query>”

                        + “<Where>”

                        + “<Eq><FieldRef Name=’FSObjType’ /><Value Type=’Integer’>1</Value></Eq>”

                        + “</Where>”

                        + “</Query>”

                    + “</View>”

};

In the above query, Scope is set to RecursiveAll. Also, I’m setting the field FSObjType=1 – this means that only folders are returned. If you want only items, set FSObjType=0. If you want both files and folders, omit it entirely.

You can actually go even further – retrieving all items from multiple lists, by enumerating through the lists and using a caml query on each. The important thing is that you only call ExecuteQuery once, at the end.

Best Practice 7. Use conditional scope to test for preconditions before loading data

Example: 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;
}  

About Krishana Kumar

Krishana Kumar is SharePoint Architect/Trainer having Architecture experience with high volumes at Enterprise level and global scale - creation of highly scalable solutions with global user base and geographically distributed architectural components. Good knowledge of SharePoint best practices and governance models. I hold Two Master degree in Computer Science with over 11 years of experience working on Microsoft Technologies specially SharePoint, Project, .NET and other Information Worker Technologies. Having good exposer in Client side scripting Angular.js, backbone and Node. I am currently responsible for SharePoint Infrastructure set up and leading teams in various medium and large scale projects, architecting, designing & installing SharePoint farms, developing custom components,, and providing advanced SharePoint administration and development training to teams and customers. I regularly speaks in various SharePoint User Groups and other Events. I have MCSA Windows Azure, MCSA Office 365, MCSE & MCSD SharePoint 2013, Microsoft Certified Developer (MCD) and holds MCPD, MCTIP and MCTS for SharePoint 2010, MCTS MOSS 2007 & WSS 3.0, MCPD, MCITP (EPM 2010 & 2007) and MCSD .NET.
This entry was posted in General Interest. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *