Blog

The challenges of building an SDK

6 min read
6 min read

The challenges of building an SDKI’m Erwin van Hunen, and I’m a Core Team member of the Office 365 Patterns and Practices team. What we do is try to help the community, together with the community, to make it easier to build solutions for Office 365 and SharePoint 2013 on-premises.

One of the things we provide the community with is what we call the Core library. This library is a .NET assembly that you can include in your own solutions that make your life much easier when it comes to using CSOM towards SharePoint.

We have a few rules when it comes to writing code for this library:

  1. All public methods, if possible, should be written as an extension method
  2. Put those extensions methods in the Microsoft.SharePoint.Client namespace
  3. Never ever change an existing method signature, but either provide overrides, or provide optional parameters.
  4. Do not create an extension method that can be easily solved in a few lines of standard CSOM code.
  5. Write readible code

In the following paragraphs I’ll go into these points in more detail.

Extension methods and the Microsoft.SharePoint.Client namespace

Every developer that wants to write CSOM will include the Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll assemblies (and likely a few more). All classes in those methods are in the Microsoft.SharePoint.Client namespace. This means that most classes will have a

using Microsoft.SharePoint.Client;

as one of the first few lines. This means that if we write our methods in the same namespace, its just a matter of including the OfficeDevPnP.Core.dll assembly and we’re set. Can’t be easier.

Now, all the methods in the Core library do something with the existing objects. Like activate a feature on a web, or add a list to a web, etc. This means that we always have a ‘base’ CSOM ClientObject which we need. Given that fact we can easily create a rule for out extension methods

public static <void|class> MethodName(this ClientObject baseObject, <Object> parameter1, etc.)

Take the above signature, and lets create a method for called AddList. Lists are by definition added to a web. So our baseObject is web. Then we need a couple of parameters, but for now let’s only take the title of the list:

public static List AddList(this web, string title)
{
    ListCreationInformation listCI = new ListCreationInformation();
    listCI.Title = title;
    ListCI.TemplateType = ListTemplateType.GenericList;
    // omitted some code
    web.Context.ExecuteQueryRetry();
}

With this extension method, it is now possible call web.AddList(“my test list”); and that is enough to create the list. No additional code, nothing. We reuse the ClientRuntimeContext from the web object to execute the query, and you’ll notice that we also have a new method called ExecuteQueryRetry(). You guessed it, ExecuteQueryRetry() is an extension method. That method by default retries a maximum of 5 times to execute the query in case your request got throttled by the server. It will wait a short period (half a second and up) between each call.

All good, but… there is a downside to this approach. CSOM can batch up several requests before you call ExecuteQueryRetry(). However, most, if not all, of our extension methods will call ExecuteQueryRetry() one or more times in the method. And that short of works against a possible batching strategy you might have.

We’re still considering on how to do handle this. For some methods we have an implementation though: we added an optional ExecuteQuery boolean parameter. The AddList method signature would then become:

public static List AddList(this Web web, string title, bool executeQuery = true)

Then instead of running web.Context.ExecuteQueryRetry(), we only run that line if executeQuery == true, otherwise we’ll leave it up to you to run ExecuteQueryRetry().

But this also comes with a challenge. Sometimes we actually need to execute the query in order for us to provide some functionality in the method. Sometimes we need to execute the query even several times in the method. If so, we can’t provide a boolean parameter which allows you to control the request. Given that fact, it means that some methods are a bit ‘chatty’ so to say. That shouldn’t be a huge problem, but it’s nevertheless good to be aware of.

Never ever change a method signature

For one simple reason: do not break old code. If you want to add parameters to an existing method, you either create an overload:

public static List AddList(this Web web, string title)
public static List AddList(this Web web, string title, ListTemplateType templateType)

or, and we like that one ever more, use a default parameter instead:

public static List AddList(this Web web, string title, ListTemplateType templateType = ListTemplateType.GenericList)

That way you don’t clutter the class with additional methods, and you also don’t break old code.

But, what if you really have to change the signature? We mark the old method then as Obsolete:

[Obsolete("Use MethodX instead")
public static List AddList(this Web web, ListTemplateType templateType, string title) // Changed the order of the method parameters

We then keep the obsolete method around for 2 releases, and then we really move it. As we distribute our code as a NuGet package, the developer will have to open the solution and Visual Studio will update the package. At compilation time the developer will then get notified that an obsolete method is being used.

Don’t go crazy, you ninja. Write readable code.

This method makes no sense:

public static void DeleteList(this List list)
{
      list.DeleteObject();
      list.Context.ExecuteQuery();
}

You’re effectively removing one line of code to write for the developer. That’s not worth it. We only add extension methods that actually abstract away complexity for the developer. Like methods that check of existance of artifacts before creating them, etc. etc. Things that are commonly done, but written over and over again by developers. That code is perfect for an extension method.

And of course you’re a C# ninja. You want to show off your amazing coding style, you want to use embedded lambdas in linq statements which call extension methods (yes… we actually do that in the core library, but not in public methods), you want to show the world your amazing coding power!

But, keep in mind that the PnP Core Library is there to help developers to get up and running. And helping comes from two sides: one side is to make it easier for the developer to write code, and the other is to help developers to understand how to do things, e.g. they can check out the extension method implementation to see how we do things. That requires that you write readible code.

A typical example is the ‘continue;’ statement. While it has its use, I do not believe it should be used deep in a method. It’s okay to use “continue” at the top of a method, as a predicate to run the code below or not. But not in second-level embedded foreach loop at the bottom of another foreach loop which also happens to be at the bottom of the method. The “continue” statement becomes an hidden trap then, and it’s so easy to miss when reading the code.

Common sense

There you have it: a few common sense rules that we use to write code of the core library. Overcome the challenges of building an SDK with ease. Do you feel like contributing? You are so welcome! You can find us at http://dev.office.com/patterns-and-practices. The code for the core library can be found at: https://github.com/officedev/pnp-sites-core. Looking forward to your submissions!

Merken

Subscribe to our newsletter