Unorthodox Architecture for Services in SharePoint Sandbox Solutions and Office 365

These days we are spending a lot of time on a series of sandbox applications that will run on Office 365. If you are a follower of this blog you know I was more than a bit disappointed in the restrictions the SharePoint 2010 sandbox implementation imposes. However, if you want to play in the sandbox, you can either just say no to requirements that aren’t directly supported, or you can earn your money and get creative!

I’ve been getting creative. These applications are going to be great – that said, some elements of the architecture are unorthodox and even a little strange. This post details one of the elements.

Goals

Expose services to external clients that:

  1. Hide their underlying technology from the caller and hide the application’s underlying implementation from prying eyes
  2. Support all current web development stacks
  3. Perform well
  4. Are easy to create and consume

Client side technologies like the Client Object Model (JSOM) and SPServices are awesome. However, they fail goal #1. If page’s business logic is implemented in JavaScript it is available to any and all who care to look. Worse, they both encourage tight coupling between the client UI and SharePoint. Both support JavaScript, and the Client Object Model also supports managed (.NET) clients and Silverlight. However, both have minor learning curves for developers who mainly write C# and use the server object model.

What we really want is for the client to consume services via an abstraction that hides the details and helps minimize dependencies. Unfortunately, you can’t deploy traditional asmx or WCF services to the SharePoint Sandbox.

Before we get into the solution, please be aware that I am not saying that this architecture is better than JSOM, SPServices, or the REST API – I’m just saying it is a better fit for our particular needs for certain specific applications. It also illustrates an interesting technique that you can add to your own arsenal.

Solution

While it is not possible to deploy traditional Web services, we can come close. A Web service is just an http endpoint to which we can pass arguments and from which we can read a response. Conceptually, the only differences between an aspx page and a Web service are the calling convention and (usually) the content type (e.g. ‘text/html’ versus ‘text/xml’ or ‘application/json’). You can create an aspx page that works just like an asmx or WCF service if you want – you just have to handle the plumbing yourself.

Our applications have lots of JavaScript and JSON is the easiest format to consume. It is easy to create a page in the sandbox that returns JSON based on query string or form POST parameters. Here’s how!

serviceContainer.master Master Page

This is a very simple master page with a single ContentPlaceHolder that the content page overrides with the service web part.

servicePage.aspx

Service pages are stored in a folder or library off the root of the web allowing a relative URL reference to serviceContainer.master. Each page uses SPUserCodeWebPart to load the a sandboxed Web Part that contains the actual implementation of the service.

A welcome byproduct of using a page in a library is the ability to use the standard SharePoint security model to control access to the page (and hence the service). This is a clear advantage for this technique compared to both the Client Object Model and SPServices.

service.webPart

The service Web part reads any required arguments from the query string and/or post parameters from the Page.Request and does its work returning the format of your choice. In the screen shot below it is returning a JSON string that contains the current Web’s Title. Because the part is stateless all of the work is done in Render().

The result looks like this:

It could just as easily be XML or any other type of content. The master page and the page that contain the Web Part are empty. What comes out of the Render method is the body of the response.

Note: Unfortunately because the Web Part lacks access to the outer page you can’t set the response content type properly. Having tested with IE, FireFox, Chrome, Safari, and Opera I can confirm that script can still consume the result without issues using jQuery.getJSON(). I have also emitted dynamically JavaScript and used the page as the src attribute in <script> elements. The response is always of type text/html. Be aware that this is a risk and may cause issues with some clients!

Performance

Testing shows that this solution provides performance that is comparable (and at times significantly better) than the equivalent Client Object Model code. The tests used a site on SharePoint Online (Office 365).

ServicePage

The service page was invoked via a .NET console application using the HttpWebRequest class. The code below shows the function that makes the call.

Fiddler shows that this call sent 1258 bytes and received 866 bytes in approximately 150 milliseconds.

Client Object Model

Two passes were tested with the JSOM from the same console application. The first initialized the ClientContext each time and the second reused an existing ClientContext. It turns out that instantiating the client context calls the sites.asmx service. Therefore each call resulted in two round trips. The important lesson learned here is that you should hold on to the context and not recreate it for the best performance. The code for the second version of the test method is shown below. To make the comparison fair, the code explicitly asks for only the Title property of the current Web.

Fiddler shows that this call sent 2022 bytes and received 724 bytes in approximately 225 milliseconds.

The results for one set of tests are as follows:

Round 1

10 calls to the sandbox page took 23544921 ticks

10 calls with the client object model took 39882812 ticks

10 calls with the client object model with one context took 22656250 ticks

 

Round 2

10 calls to the sandbox page took 21337890 ticks

10 calls with the client object model took 40322266 ticks

10 calls with the client object model with one context took 30048828 ticks

 

Round 3

10 calls to the sandbox page took 17460938 ticks

10 calls with the client object model took 43066406 ticks

10 calls with the client object model with one context took 22109375 ticks

 

Round 4 (With Fiddler On)

10 calls to the sandbox page took 15449219 ticks

10 calls with the client object model took 38222656 ticks

10 calls with the client object model with one context took 24550782 ticks

 

Round 5

100 calls to the sandbox page took 171845703 ticks

100 calls with the client object model took 473261719 ticks

100 calls with the client object model with one context took 240097656 ticks

Conclusion

In the first round the JSOM performed slightly better than the service page. In subsequent rounds the service page’s performance was significantly better. I speculate that the differences are due to unknown factors on my test Office 365 tenancy. Therefore, the only conclusion I am willing to stand by is that using this technique is at worst performance neutral but that it might be better.

The tests used a trivial scenario. In spite of the excellent batching capabilities found in the JSOM, real world scenarios often require multiple roundtrips where each operation depends on data from a previous operation that is not on the client. This architecture potentially offers much better performance by combining such operations into a single round trip.

Author: Doug Ware