Smarter ideas worth writing about.

Developing for Multiple Environments in SharePoint Online

The transition from developing for SharePoint on-premise to SharePoint Online (SPO) contains many hurdles that need to be overcome; switching to client-side object model, implementing custom branding without custom masterpages, writing webparts in JavaScript with the removal of managed code, just to name a few. However, one of the major hurdles is writing code that can isolate a SPO site collection as its own environment due to architectural differences with on-premise.

This blog will cover the following topics to help a developer prepare for development in SPO:

  • On-Premise vs SPO Architecture
  • Environment Identification in SPO
  • Using Shared Service Applications for Custom Functionality
  • Querying Lists and Search Service

On-Premise vs SPO Architecture

The normal practice for a SharePoint on-premise farm is to have different servers for the various environments: Development, QA, Production. Each one of these environments reside on different servers and are independent. This means each has their own set of service applications developers can leverage (Search, User Profiles, Managed Metadata, etc.).

Based on this architecture a developer can make more assumptions about where a site collection will reside, which normally is at the root of a Web Application. Each environment will have at least one Web Application with a unique host url to distinguish them from one another. With this isolation, the information on one environment will not appear in another environment. The image below illustrates the environment separation.

Figure A: SharePoint On-Premise environments

The environment architecture is different in SPO. A company usually gets a single tenant to create site collections. A SPO tenant can be compared to an on-premise web application (without alternate access mappings) because they both have a single host url. This requires a developer to make sure they are querying lists on the current site collection rather than assuming they can use the root site collection of the web application.

Also, each tenant has only one instance of the service applications that exist on the servers of each environment when on-premise. Resulting in all environments sharing the service application and it is the developer’s responsibility to make sure the correct data is being displayed. The image below illustrates the same environment setup as before, but this time in SPO.

Figure B: SharePoint Online Environments

Environment Identification in SPO

Sometimes a solution needs to know what environment it is currently running in so it can adjust functionality, such as turning off caching in non-production environments. Since the host name is no longer unique between environments there needs to be another way to determine where the code is running.

A way to achieve this is to dynamically change the contents of their code before it is provisioned to a site. Using remote provisioning allows for this file manipulation. The example below shows how to use a .NET website with a web.config transforms on an AppSetting to perform this action.

Setting up web.config transforms

Visual Studio has a built in web.config transformer that allows for manipulation of the web.confg file when it is published under the different configurations. The default configurations of Debug and Release will be used for this example. The current configuration can be changed via the dropdown in the visual studio toolbar.

Figure C: Visual Studio Configuration dropdown

Below are the XML entries that will reside in their respective config file. In this case the “value” attribute of the appSettings with the key of “Environment” will be adjusted based on the Visual Studio configuration selected.

Figure D: Visual Studio web config with transformations

Web.config

<configuration>
<appSettings>
    	<add key=“Environment” value=“DEV” />
  </appSettings>
</configuration>

Web.Debug.config

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="Environment" value="QA" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
  </appSettings>
</configuration>

Web.Release.Config

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="Environment" value="PROD" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
  </appSettings>
</configuration>

Manipulation of JavaScript file

Once these web.config manipulations are in place the code that provisions files to the SharePoint site can be used to modify the contents of a JavaScript file to create an environment JSON object (CSGEnvironment) that can be utilized by code written for the site.

JavaScript file Before Modification

var CSGEnvironment = {
    Environment: "{ENVIRONMENT}",
};

JavaScript file After Modification for Production deployment

var CSGEnvironment = {
    Environment: "PROD",
};

C# code to modify file

private static System.IO.Stream ReplaceFileTokens(string filePath) 
{
            	string fileText = System.IO.File.ReadAllText(filePath);
            	
string currentEnvironment = WebConfigurationManager.AppSettings["Environment"];
fileText = fileText.Replace("{ENVIRONMENT}",  "PROD");  

              	byte[] bytes = System.Text.Encoding.ASCII.GetBytes(fileText);
            	System.IO.Stream ms = new System.IO.MemoryStream(bytes);
            	return ms;
} 

Using Shared Service Applications for Custom Functionality

As previously called out, all environments will share the SharePoint services on a SPO tenant. This section will discuss approaches that can be taken to keep your environments separated inside those shared services.

Terms Store/User Profile services

Often a developer will use the Term Store and User Profile services as part of custom functionality. Common use cases would be cross site collection navigation using a term set in the Term Store or user personalization on the site using a custom user profile property to store configurations.

When the development is done on-premise the same name can be used across environments for these items due to the isolation of the service applications between environments (see Figure A). In SPO this is not the case since these service applications are shared (see Figure A), which means term sets and user profile properties should be created for each environment with different names. To make sure the code is accessing the proper items in the services the same environment identifier approach with web.config transforms, as outlined above, can be used to specify the correct name at deployment time.

JavaScript file Before Modification

var CSGEnvironment = {
    Environment: "{ENVIRONMENT}",
    TermSetGlobalNav: "{TERM_SET_GLOBAL_NAV}",
    UserProfilePersonalize : "{USER_PROFILE_PERSONAL}"
}; 

JavaScript file After Modification for Production deployment

var CSGEnvironment = {
    Environment: "PROD",
    TermSetGlobalNav: "CSGGlobalNav-Prod",
    UserProfilePersonalize : "CSGPersonalize-Prod"
};

Querying Lists and Search Service

When retrieving data from lists and search developers need to actively specify where they will like the data to be pulled from. The section will provide some recommendation on how this can be accomplished.

Querying Lists

Due to each environment being a separate site collection it is important to make sure list querying being done is at the correct scope. When using the REST API to get list data the request url can dynamically be adjusted prior to the REST call being made. The JavaScript code snippets below show how this can be done to generate a call at the host, site collection, and web levels.

var restURLHost = getHostUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title”
//restURLHost: https://company.sharepoint.com

var restURLSiteCollection = getSiteCollectionUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title"
//restURLSiteCollection: https://company.sharepoint.com/sites/SiteCollectionName

var restURLWeb = getWebUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title"
//restURLSiteCollection: https://company.sharepoint.com/sites/SiteCollection/Subsite



function getHostUrl() {
    return window.location.protocol + "//" + window.location.host;
};

function getSiteCollectionUrl() {
    return getHostUrl() + _spPageContextInfo.siteServerRelativeUrl;

};
function getWebUrl() {
    return getHostUrl() + _spPageContextInfo.webServerRelativeUrl;
}; 

Search Service

Since SPO only contains one search service that means the data for all environments will reside in a single search index. Search is a powerful tool inside SharePoint to get data across site collections and sites. To make sure the proper data is being retrieved the “Path” managed property can be used in the query to guarantee search returns the data from the desired sites. The syntax being used is for the example is Keyword Query Language (KQL). The following query condition will only return data from the development site collection (/sites/dev).

Path:https://company.sharepoint.com/sites/dev

For custom code using the search API to get the current environment’s url the same functions in the Query Lists section can be used to dynamically change the query.

When using out of the box search webparts, like Search Results or Content Search, SharePoint provides a “Site” token that can be placed into the query to get the URL of the current site. The webpart will replace that token with the correct information prior to running the query.

Path:{Site.URL}

The screenshot below is the edit query window for the out of the box Content Search webpart. The query has been modified to return any document or list item on the current site that contains the word “Benefits” in the Title will be returned.

Figure E: Content Query webpart screenshot

There are many other tokens that can be used as well, see link in References at the end of this blog.

Summary

Understanding the architecture of your SPO environment at the start of development will eliminate some of the headaches encountered the first-time code gets promoted to a QA or Production environment. Below are the key items to remember:

  • Modify code files at deployment time to so specify the environment information.
  • SharePoint servers that were previously isolated between environment are now shared in SPO, requiring more forethought and additional work on the developer to make sure to display the correct data.
  • When querying lists make sure the correct site collection or web’s url is being used since SPO has a single host name and environments need to be site collections rather than web applications


References

Share:

About The Author

SharePoint Developer
Frank has been a SharePoint developer since SharePoint 2010 was first released.  He has worked on a variety of SharePoint implementations, including intranets, extranets and public sites.