Sep.10

Configuring Log4Net for use with an Azure Cloud Service

This post is nothing more than just a quick write up of getting Log4Net – your favorite logging framework, otherwise you wouldn’t be reading this – to work in Microsoft’s Azure Cloud. Ah, well … Let me be more precise: Get it to work primarily locally on your development box, where Azure is nicely emulated.

Assuming you’ve just created a new Azure Cloud Service Project with a WebRole (I’ve picked the WCF Service Library from the available Templates) you should:

First add Log4Net to your Web.Config as follows

<configsections  >
        <section name="TraceAppender" type="log4net.Appender.TraceAppender"  >
</configsections>
<log4net>
  <appender name="TraceAppender" type="log4net.Appender.TraceAppender">
    <immediateflush value="true">
    <layout type="log4net.Layout.PatternLayout">
         <conversionpattern value="%date %level %thread %logger - %message%newline">
    </layout>
  </appender>
  <root value="ALL">
    <level ref="TraceAppender">
    <appender-ref>
  </root>
</log4net>

Secondly configure the TraceListener in the Web.Config as follows

<system.diagnostics>
 <trace>
  <listeners>
   <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="AzureDiagnostics">
    <filter type="" />
   </add>
  </listeners>
 </trace>
</system.diagnostics>

Thirdly configure the sources for traces in the WebRole’s OnStart()

public override bool OnStart()
{
var diagConfig = DiagnosticMonitor.GetDefaultInitialConfiguration();

var directories = diagConfig.Directories;
var infrastructureDiagnostics = diagConfig.DiagnosticInfrastructureLogs;
var applicationLogs = diagConfig.Logs;
var eventLogs = diagConfig.WindowsEventLog;

applicationLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
infrastructureDiagnostics.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
eventLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);

infrastructureDiagnostics.ScheduledTransferLogLevelFilter = LogLevel.Undefined;
applicationLogs.ScheduledTransferLogLevelFilter = LogLevel.Undefined;
eventLogs.ScheduledTransferLogLevelFilter = LogLevel.Undefined;

DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", diagConfig);

// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.

return base.OnStart();
}

Last but not least: Initialize the XmlConfigurator in your AssemblyInfo.cs

[assembly: log4net.Config.XmlConfigurator(Watch = true)]

But why does this work (on a local development box)?

  1. Because the DiagnosticMonitor was started with “Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString” and for the local development box this value is set to “UseDevelopmentStorage=true” (which you can see for yourself in the settings file ServiceConfiguration.local.cscfg)
  2. Because we configured an Application Log and told it to “flush” (=transfer) once every minute its buffers (into an Azure table)
  3. Because in the Web.Config we configured Log4Net to use the log4net.Appender.TraceAppender
  4. Because we have patience and wait a few minutes upon the first time testing (because if the Azure Table does not exist it needs to be created – See for yourself by right-clicking on the emulator icon in your system tray and follow the “console” output of the Compute Emulator UI)
  5. Because we know that we need to click Server Explorer in Visual Studio, followed by Azure > Storage > (Development) > Tables > WADLogsTable to find the Log Entries
Uncategorized

Aug.27

Creating WebTemplate for SharePoint 2013 (OnPrem)

I was surprised to find that the creating a WebTemplate (instead of a full blown Site Definition) is so poorly documented. There are a couple of really extensive blogs around, but still they don’t unveil the utmost important detail of how to provision the ONET.XML whilst avoiding receiving a 0x8107026E error. If this is the reason you’re here then you better make sure that you named the SharePoint Emtpy Element Item so that it matches your WebTemplate’s name. If you do receive this error (and according to Google, many do) a quick analysis of SharePoint’s ULS Log does give away the clue: Failed to open the file ‘C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions15TemplateFeaturesTeamSiteTemplate_MyFancyWebTemplateFeatureMyFancyWebTemplateonet.xml’. From this it follows that SharePoint looks in the Feature’s folder for a sub folder that bears the name of the WebTemplate and expects to find the ONET.XML file in this folder. This immediately brings to attention another important fact. The ONET.XML file should be added to Empty Element item in your Visual Studio Project. This is a little bit worrying because normally you’d want to introduce files to SharePoint by adding a module. However, in this case you add the ONET.XML below your Empty Element item in which you define your WebTemplate. To ensure that the ONET.XML file gets deployed to the feature’s folder on the drive you need to change it’s Deployment Type property to Element File. Once this is done you should be able to deploy your WebTemplate using a Farm Scoped Feature and it will generally available when creating new sites.

One last interesting thing here is the fact that you cannot use WebTemplates for Feature Stapling. But if we provide our own ONET.XML we really don’t miss this, because we can activate the features here! Even though this is not the only contraint, WebTemplates are a rather helpful instrument for creating custom Site Templates without losing the upgradeability completely. This is mainly achieved because they’re based on out-of-the-box templates e.g. STS#0. However, to use them when creating sites using PowerShell, you’ll need to get accustomed to rather akward naming conventions. The Template ID of your WebTemplate is a concatenation of the Feature ID and the WebTemplate name e.g. “{11111111-1111-1111-1111-111111111111}#MyFancyWebTemplate”.

SharePoint

Aug.08

Develop a Provider-Hosted SharePoint App for hybrid Site Collection provisioning

Introduction

Using the steps below, I was able to develop a Provider-Hosted SharePoint App for hybrid Site Collection provisioning i.e. in SharePoint Online as well as on premise. The Provider-Hosted SharePoint App will act as a “stub” for a WCF (Web) Service. This service will be called from a Remote Event Receiver that has been added to a list in the App Web. Once triggered, the Provider Hosted App will make an app-only call back to SharePoint using the C# CSOM to create a new Site Collection.

provider hosted app

Important

The following is only valid for a single-server development environment i.e. SharePoint and Visual Studio are installed on the same (virtual) machine.

Prerequisites

  1. SharePoint 2013 SP1 is installed
  2. SharePoint 2013 CU April 2014 is installed (this will update the CSOM so that Site Collections can be created on premise much in the same way this was before already possible for SharePoint Online).

Step 1: The security plumbing (SSL, Certificate, Token Issuer)

A Provider-Hosted App needs a lot of (security) plumbing to ensure that information is exchanged in a secure way and only by authorized users and apps. Since this is a development-environment, SSL is optional. But of course for any production or staging environment you’ll want to add this extra security layer.

Note Also, when you finally publish your app, you have to define your app’s start page and the VS-Wizard requires this address to start with HTTPS. Since you don’t know the URL of your app at development time, you’ll need to create a (self-signed) SSL certificate for the IIS web application that hosts your provider hosted app.

  • Since this is a development environment I can create the required X.509 certificate that I need to provide a mechanism for encryption for the Server to Server communication myself. For a production or staging environment you obviously need to buy a certificate from a recognized issuer. To create such a certificate you have basically two options:

    For the default website without any host name
    You can use IIS Manager > Server > Server Certificates > Create Self-Signed Certificate and follow the description here http://msdn.microsoft.com/en-Us/library/office/fp179901(v=office.15).aspx

    For a host named website
    b. download the IIS 6.0 Resource Kit Tools and use SelfSSL to create a Self-Signed Certificate. More infos I found here: http://www.sslshopper.com/article-how-to-create-a-self-signed-certificate-in-iis-7.html
    To create a certifcate for my website with host name “my-sharepoint.is-sp2013.local” with SelfSSL I used:

    C:\>SelfSSL /N:CN=my-sharepoint.is-sp2013.local /V:1000
  • Save the certificates locally (e.g. here: “C:\DEV\Certs\HighTrustAppCert.cer” (= public key) and “C:\DEV\Certs\HighTrustAppCert.pfx” (=private key)) and create a new Token Issuer for it and register it it as a trusted Token Issuer with SharePoint’s Secure Token Service using the following script.
    $publicCertPath = "C:\DEV\Certs\HighTrustAppCert.cer"
    $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($publicCertPath)
    
    New-SPTrustedRootAuthority -Name "HighTrustAppCert" -Certificate $certificate
    
    $realm = Get-SPAuthenticationRealm
    
    $specificIssuerId = "11111111-1111-1111-1111-111111111112"
    $fullIssuerIdentifier = $specificIssuerId + '@' + $realm
    
    New-SPTrustedSecurityTokenIssuer -Name "High Trust App Cert" -Certificate $certificate -RegisteredIssuerName $fullIssuerIdentifier –IsTrustBroker
    iisreset
    
    $serviceConfig = Get-SPSecurityTokenServiceConfig
    $serviceConfig.AllowOAuthOverHttp = $true
    $serviceConfig.Update()
    

    Issuer ID This is a randomly picked GUID. If you pick your own, make sure to only use lower case characters. You can use this issuer (of secure tokens for your provider hosted app) for more than app. So make sure you’ll copy the output of this script and save it for later.

Step 2 – Create a provider hosted app

  • Create a SharePoint App in Visual Studio using the appropriate template and select “Provider Hosted”. Note how a second (Web Application) project was created automatically by Visual Studio. This is the project that is the provider hosted application. For now we can leave it as it is. Don’t remove or delete it yet. We’ll cross that bridge when we come to it.
  • Open the AppManifest.xml and make the following edits

<?xml version="1.0" encoding="utf-8" ?>
<!--Created:cb85b80c-f585-40ff-8bfc-12ff4d0e34a9-->
<App xmlns="<a href="http://schemas.microsoft.com/sharepoint/2012/app/manifest">http://schemas.microsoft.com/sharepoint/2012/app/manifest</a>"
     Name="WorkspaceManager"
     ProductID="{b2c6212d-4499-40f1-a9d4-4f66ad1dec75}"
     Version="1.0.0.0"
     SharePointMinVersion="15.0.0.0"
>
  <Properties>
    <Title>Workspace Manager</Title>
    <StartPage>~appWebUrl/Pages/index.html?{StandardTokens}</StartPage>
  </Properties>

  <AppPrincipal>
    <RemoteWebApplication ClientId="226ea038-b455-4e18-b269-43ff36a2730e" />
  </AppPrincipal>
  <AppPermissionRequests AllowAppOnlyPolicy="true" >
    <AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
  </AppPermissionRequests>
</App>

Pay special attention to
RemoteWebApplication Makes the App into a Provider hosted App

ClientId The Client ID is a GUID that’s unique to the App and that is for all installed instances in all SharePoint Farms globally the same. You need to tell your SharePoint environment that you’ll like to install a remote application by registering it. You can do this using the registration tool: /_layouts/15/appregnew.aspx. Don’t be confused by “Generate” buttons. They can generate a GUID for you but normally you’ll want Visual Studios Tool to do so and register your Client ID in SharePoint and not the other way around.

AllowAppOnlyPolicy Setting this to true, enables the App to be called without a user being logged in. For our scenario, where we want to asynchronously call back into SharePoint, this is a requirement.

  • I want my app to integrate in the SharePoint user experience. The user should be able to enter all required data into a form for a regular SharePoint list. Luckily it’s still possible to create an App Web in SharePoint with some lists etc. for the provider hosted app. Hence finalize the App by adding Columns, ContentTypes and Lists as required. To provision Site Collections I want the user to provide me with a few details like Title, Tenant Admin Uri, Target (online or on premise), Site owner, Template and URL so I created a ContentType for this purpose and based on this ContentType created a list for it.

Step 3 – Create a new DLL Project (for the WCF service)

  • Now I want to implement a List Item Event that is triggered each time when a new entry is added to the list. To accomplish this, I need to add a Remote Event Receiver to the project. Doing so and naming it WorkspaceAdded will automatically add a WCF service to the Web Application project in the solution.
  • Actually, I don’t need the Web Application. I only need the WCF service. Hence I remove the Web Application and instead create a new DLL Project for the WCF service and add a couple of files from the Web Application project back to this project:

Web.Config You want to keep this (Must be in the root of the project), because it has all the plumbing you need for the WCF service as well as a couple of application settings needed for the service to take the identity of the registered provider hosted app:

<appSettings>
  <add key="ClientId" value="7ca21fdf-eaaf-452e-b2ba-8d56ef7c6066" />
  <add key="ClientSigningCertificatePath" value="C:\DEV\Certs\HighTrustAppCert.pfx" />
  <add key="ClientSigningCertificatePassword" value="------" />
  <add key="IssuerId" value="11111111-1111-1111-1111-111111111112" />
</appSettings>

TokenHelper.cs The TokenHelper class is a little gift from Microsoft that deals with the complexity of obtaining and exchanging an oauth token.
SharePointContext.cs For this example we don’t need this class but in the near future you’ll probably find good use for this.
WorkspaceAdded.cs The class implementing the WCF service.
WorkspaceAdded.svc The WCF host file.

  • To wire up the app project with the DLL project, some “hocus pocus” is required. Visual Studio doesn’t like the idea of a DLL project being the Web Project that is linked with the app. But if no Web Project is linked with the app, Visual Studio will assume a “manifest-only” deployment scenario and publish the app with a wrong profile. Hence I quickly unload the project und edit the .csproj file by adding (or updating) the following snippet:

<ItemGroup>
    <ProjectReference Include="..\WorkspaceManager\WorkspaceManager.csproj">
      <Project>{1F32CD07-35F3-42BF-8A99-B1E231DDF966}</Project>
      <Name>WorkspaceManager</Name>
      <Private>True</Private>
      <RoleType>Web</RoleType>
      <OutputItemType>SharePointWebProjectOutput</OutputItemType>
      <RoleName>WorkspaceManager</RoleName>
      <ReferenceOutputAssembly>False</ReferenceOutputAssembly>
    </ProjectReference>
</ItemGroup>

Update the ProjectReference and Project GUID as needed.

Step 4 – Enable Remote Site Collection creation for SharePoint

  • Enable Self-Service Site Collection Creation using the Central Admin
  • Install Windows PowerShell for SharePoint Online to install the correct version of the Microsoft.Online.SharePoint.Client.Tenant.dll here http://www.microsoft.com/en-us/download/details.aspx?id=35588
  • Enable Remote Site Collection Creation for the target Web Application using PowerShell

# Enable the remote site collection creation for on-prem in web application level
 # If this is not done, unknon object exception is raised by the CSOM code
 #
 $WebApplicationUrl = "http://my-sharepoint.is-sp2013.local"
 $snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}
 if ($snapin -eq $null)
 {
     Write-Host "Loading SharePoint Powershell Snapin"
     Add-PSSnapin "Microsoft.SharePoint.Powershell"
 }   
    
 $webapp = Get-SPWebApplication $WebApplicationUrl
 $newProxyLibrary = New-Object "Microsoft.SharePoint.Administration.SPClientCallableProxyLibrary"
 $newProxyLibrary.AssemblyName = "Microsoft.Online.SharePoint.Dedicated.TenantAdmin.ServerStub, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
 $newProxyLibrary.SupportAppAuthentication = $true
 $webapp.ClientCallableSettings.ProxyLibraries.Add($newProxyLibrary)
 $webapp.Update()
 
 Write-Host "Successfully added TenantAdmin ServerStub to ClientCallableProxyLibrary."
 
 # Reset the memory of the web application
 
 Write-Host "IISReset..."   
 Restart-Service W3SVC,WAS -force
 Write-Host "IISReset complete."   

3. Set a "fake" Tenant Administration Site e.g. the root address of the target web application using PowerShell as follows

# Set admin site type property to the site collection using PS for any site collection type. # This is needed to be set for the site collection which is used as the # "Connection point" for the CSOM when site collections are created in on-prem # $siteColUrl = "http://my-sharepoint.is-sp2013.local" $snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'} if ($snapin -eq $null) { Write-Host "Loading SharePoint Powershell Snapin" Add-PSSnapin "Microsoft.SharePoint.Powershell" } $site = get-spsite -Identity $siteColUrl $site.AdministrationSiteType = [Microsoft.SharePoint.SPAdministrationSiteType]::TenantAdministration

If this Site Collection is deleted, this script needs to run again.

Step 5 – Implementing the Remote Site Collection Service

  • What follows is part of the code of my prototype implementation

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.EventReceivers;
using log4net;
using System.ServiceModel.Activation;
using System.ServiceModel;
using Microsoft.SharePoint.Client.Services;
using System.Security;
using System.Web.Configuration;
using My.SharePoint.Client.RemoteSiteCollection;

namespace My.SharePoint.WorkspaceManager.Services
{
    [BasicHttpBindingServiceMetadataExchangeEndpointAttribute]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class WorkspaceAdded : IRemoteEventService
    {
        private static ILog log = LogManager.GetLogger(typeof(WorkspaceAdded));

        /// <summary>
        /// Handles events that occur before an action occurs, such as when a user adds or deletes a list item.
        /// </summary>
        /// <param name="properties">Holds information about the remote event.</param>
        /// <returns>Holds information returned from the remote event.</returns>
        public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties rep)
        {
   // Not implemented
        }

        /// <summary>
        /// Handles events that occur after an action occurs, such as after a user adds an item to a list or deletes an item from a list.
        /// </summary>
        /// <param name="properties">Holds information about the remote event.</param>
        public void ProcessOneWayEvent(SPRemoteEventProperties rep)
        {
            try
            {
                var tenantAdminUrl = rep.ItemEventProperties.AfterProperties["TenantAdminUrl"].ToString();
                var targetString = rep.ItemEventProperties.AfterProperties["Target"].ToString();
                var credentials = GetCredentials(properties.TenantAdminUrl, properties.Target);
                var remoteSiteCollectionUrl = rep.ItemEventProperties.AfterProperties["TargetUrl"].ToString();
                var title = rep.ItemEventProperties.AfterProperties["Title"].ToString();
    var template = rep.ItemEventProperties.AfterProperties["Template"].ToString();
    var ownerAccountString = rep.ItemEventProperties.AfterProperties["OwnerAccount"].ToString();
    
    // properties is an object to hold all variables created before - Code not shown

                Manager.Create(properties, properties.Target, credentials);
            }
            catch (Exception e)
            {
                log.Error("Error occured @ProcessOneWayEvent: " + e.Message + "\n" + e.StackTrace);
            }
        }

        private static SharePointOnlineCredentials GetCredentials(String siteUrl, Targets target)
        {
            SharePointOnlineCredentials credentials = null;

            if (target == Targets.Online)
            {
                var userName = WebConfigurationManager.AppSettings.Get("O365UserName") ?? String.Empty;
                var pwd = WebConfigurationManager.AppSettings.Get("O365Password") ?? String.Empty;
                SecureString sPwd = GetSecurePassword(pwd);

                /* End Program if no Credentials */
                if (String.IsNullOrEmpty(userName) || (pwd == null))
                {
                    throw new ArgumentException("You entered wrong credentials for SharePoint Online...");
                }

                credentials = new SharePointOnlineCredentials(userName, sPwd);
            }

            return credentials;
        }

        private static SecureString GetSecurePassword(String pwd)
        {
            SecureString sStrPwd = new SecureString();
            try
            {
                foreach (char chr in pwd.ToCharArray())
                {
                    sStrPwd.AppendChar(chr);
                }
            }
            catch (Exception e)
            {
                sStrPwd = null;
                log.Error(e.Message);
            }

            return sStrPwd;
        }
    }
 
 public static class Manager
    {
        private static ILog log = LogManager.GetLogger(typeof(Manager));

        public static void Create(Properties properties, Targets target, SharePointOnlineCredentials credentials = null)
        {
            switch (target)
            {
                case Targets.Online:
                    if (credentials == null)
                    {
                        throw new ArgumentNullException("SharePoint Online Credentials are missing...");
                    }
                    CreateOnline(properties, credentials);
                    break;
                case Targets.OnPrem:
                    CreateOnPrem(properties);
                    break;
                default:
                    break;
            }
        }

        private static void CreateOnline(Properties properties, SharePointOnlineCredentials credentials)
        {
            ClientContext ctx = new ClientContext(properties.TenantAdminUrl);
            ctx.AuthenticationMode = ClientAuthenticationMode.Default;
            ctx.Credentials = credentials;

            var tenant = new Tenant(ctx);

            var newSite = new SiteCreationProperties()
            {
                Url = properties.RemoteSiteCollectionUrl,
                Owner = properties.OwnerAccountString,
                Template = properties.Template,
                Title = properties.Title,
                StorageMaximumLevel = properties.StorageMaximumLevel,
                StorageWarningLevel = properties.StorageWarningLevel,
                TimeZoneId = properties.TimeZoneId,
                UserCodeMaximumLevel = properties.UserCodeMaximumLevel,
                UserCodeWarningLevel = properties.UserCodeWarningLevel
            };

            var spoOperation = tenant.CreateSite(newSite);

            ctx.Load(spoOperation);
            ctx.ExecuteQuery();

        }

        private static void CreateOnPrem(Properties properties)
        {
            var tenantAdminUri = new Uri(properties.TenantAdminUrl);

            string appOnlyAccessToken = TokenHelper.GetS2SAccessTokenWithWindowsIdentity(tenantAdminUri, null);

            using (var ctx = TokenHelper.GetClientContextWithAccessToken(tenantAdminUri.ToString(), appOnlyAccessToken))
            {
                var tenant = new Tenant(ctx);
                var newSite = new SiteCreationProperties()
                {
                    Url = properties.RemoteSiteCollectionUrl,
                    Owner = properties.OwnerAccountString,
                    Title = properties.Title,
                    Template = properties.Template
                };

                //start the SPO operation to create the site
                var spoOperation = tenant.CreateSite(newSite);

                ctx.Load(spoOperation);
                ctx.ExecuteQuery();
            }
        }
    }
}

In some blogs (e.g. here http://blogs.msdn.com/b/vesku/archive/2014/06/09/provisioning-site-collections-using-sp-app-model-in-on-premises-with-just-csom.aspx) you may find non app-only code, but this a. complex and b. not what you want…

http://blogs.msdn.com/b/vesku/archive/2014/06/09/provisioning-site-collections-using-sp-app-model-in-on-premises-with-just-csom.aspx

  • Now you can deploy the service by adding its DLL to GAC using a post build event as follows:

“C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\gacutil.exe” /i “$(TargetDir)$(TargetFileName)”

  • Don’t forget to also add
    C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.Client.Tenant.dll

    to the GAC (or at least enable the service to load it from another location).

  • Create an SSL endpoint for the provider hosted app. For this I created an IIS Web Application and named it wcf.is-sp2013.local. I created a virtual directory below it and called it WorkspaceAdded. Using IIS Manager I turned this directory into an application. Using SelfSSL I created an SSL certificate for it. I copied the WorkspaceAdded.svc and Web.Config file.
  • Last but not least I added a redirect.aspx file in the root of this Web Application. It will be the splash page for the provider hosted app. The challenge here is that the provider doesn’t know the full URL of the app at development time. Only after installation of the app its full URL is known. However, when the user’s launches the app, the start page is called and you can configure it to be called with all tokens (like host web url, app web url etc.). So a little JavaScript can help here. Here’s my redirect.aspx (it needs to be .aspx because SharePoint will call the start page using HTTP POST and not a simple GET):

<!DOCTYPE html>
 
 <head>
     <meta http-equiv="X-UA-Compatible" content="IE=Edge">
     <title>Workspace</title>
 </head>
 
 <html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>">
 <head runat="server">
     <title>Welcome</title>
 </head>
 <body>
  <p>
   <span id="hostWebUrlPlaceHolder"></span>
  </p>
 
  <script type="text/javascript">
   function getQueryStringParameter(paramToRetrieve) {
    var params = document.URL.split("?")[1].split("&");
    for (var i = 0; i < params.length; i = i + 1) {
     var singleParam = params[i].split("=");
     if (singleParam[0] == paramToRetrieve)
      return singleParam[1];
    }
   }
   
   var hostWebUrl = getQueryStringParameter("SPAppWebUrl");
   console.log(hostWebUrl);
 
   var a = document.createElement('a');
   var linkText = document.createTextNode("Launch App...");
   a.appendChild(linkText);
   a.title = "Launch App...";
   a.href = unescape(hostWebUrl);
   var placeHolder = document.getElementById('hostWebUrlPlaceHolder');
   placeHolder.appendChild(a);
 
  </script>
 </body>
 </html>

This page will simple show a link that when clicked will redirect the user back to the Workspace List in the App Web.

Step 6 – Wiring up the app and the provider hosted app

  • Then I added the remote event receiver to the project, Visual Studio added an elements.xml to the project. Open it and edit so that the URL points to the new service

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="<a href="http://schemas.microsoft.com/sharepoint/">http://schemas.microsoft.com/sharepoint/</a>">
  <Receivers ListTemplateId="100">
    <Receiver>
      <Name>WorkspaceAdded</Name>
      <Type>ItemAdded</Type>
      <SequenceNumber>10010</SequenceNumber>
      <Url>https//wcf.is-sp2013.local/WorkspaceAdded/WorkspaceAdded.svc</Url>
    </Receiver>
  </Receivers>
</Elements>

  • Left-click the project and choose publish. Now you can set the app identity and store it a profile in the project by entering details like Token Issuer, Client Id etc. you previously generated to create and register the Token Issuer.

app settings

Note If Visual Studio cannot find the Web Application project or the Web.Config file in the root of the Web Application project it will show a different dialog (for only publishing the app manifest).

  • Continue by clicking “Package the app” and enter the Startup Page (and again the Client ID).

package settings

Step 7 – Deploy the app at Tenant Scope

  • Because this app requires Tenant Scoped permissions it needs to be deployed at Tenant Scope. This can be achieved by uploading the published .app file in the SharePoint App Catalogue first and then install the app in the App Catalogue. This seems a bit strange at first, but once the app is installed in the App Catalogue, it can be deployed to other Workspaces.

deployment

To deploy the app for example to http://my-sharepoint.is-sp2013.local simply click DEPLOYMENT, add the URL to the collection by entering it in the box for “Enter a site collections to deploy to:”, click Add and OK to confirm.

Further notes

1. To get Log4net working in a WCF service I added its configuration to the Web.Config instead of in own file that would be referenced in the assembly file. Apparently log4net is loaded to late in such cases.
2. Provider hosted apps can only be deployed at Tenant Scope.
3. Force IE to use Standards Mode by adding
<meta http-equiv=”X-UA-Compatible” content=”IE=Edge”>

Uncategorized

Nov.12

A simple TypeScript Pattern for more synchronize asynchronous programming

Now that the days have become shorter and it’s too cold for long nights on the terrace, I had some time available to play experiment with TypeScript. On CodePlex you’ll find a sample project that illustrates how you can use TypeScript for a more synchronized approache to asynchronous programming.

https://asyncobservable.codeplex.com/

The AsyncObservable pattern is in fact a simple App that illustrates the power of TypeScript. Not that TypeScript is more powerful than JavaScript. But it helps to apply patterns used in object oriented programming when building powerful and robust JavaScript Apps.

The idea is simple and based on the Observable pattern and it’s no coincidence that it bears great resemblance to the KnockOut.js pattern.

The basic idea is that each asynchronous call to a service will result in a data set or more likely a JSON object e.g.

var egg = GetSomethingFromChicken();

Unfortunately you cannot write code like this when the call is made asynchronous because the method call will return nothing. You’ll need to define a delegate for this that will handle the callback as soon as the data is returned asynchronously.

With the AsyncObservable pattern, however, you can write code that bears some resemblance to the example above:

// Dependency of type SP.Taxonomy.TermStore
var egg= new Dependency<Egg>();

In this case the variable egg is defined as a Dependency object of type T as follows:

class Dependency<T>

Such Dependency classes will have a property of type Array<AsyncObserver> and here the Dependency stores a reference to object (that is an Observer) that created the Dependency in the first place.

Then through some plumbing, the Dependency is added to a queue of Dependencies that is managed by the Observer. Once all Dependencies have been added to the queue, its Enumerator-like behavior can be kickstarted like this:

// Now put the wheel in motion
if (this.MoveNext()) {
this.ResolveCurrent();
}

The MoveNext() method now points to the first Dependency object in the array of Dependencies and the ResolveCurrent() method will call the Resolve method on the Dependency object. This is the method that should be overwritten when the Dependency class is inherited from. It’s in fact the usual callback method.

To keep the wheel in motion it is essential that upon a successful callback the Dependency object notifies its Observer by calling the Resolved() method on itself.

Have a look at the code and any feedback and suggestions are highly appreciated.

Ps, since I’m a SharePoint Architect it will come at no surprise that the example code is for SharePoint. But it’s easy to see that this example is in fact not dependent on SharePoint technology. And I’m also aware that SharePoint can actually execute a batch of queries.

Uncategorized

Nov.09

Using TypeScript for SharePoint App Development

This post is the first one in a series on developing SharePoint 2013 Apps using TypeScript. I’ve become a big fan of TypeScript, as it allows me to write cleaner JavaScript code, inspires me because I can think in object oriented patterns like for example Observable etc., helps me organize (and re-use) my code in libraries and gives me the full power of Intellisense in Visual Studio (Express). This first post is about setting up your development environment. No code will be written yet. We’ll come to this later.

With the release of TypeScript 9.1.1 things have become slightly more “difficult” for SharePoint developers. The generated .js file is no longer available directly under the .ts file we’re creating. This means that it’s also not automatically included in the package. Also, the usage of a require framework like CommonJS or require.js is now mandatory.

Step 1 – Download the TypeScript Plugin for Visual Studio

Head over to http://www.typescriptlang.org/#download and follow the instructions to download the plugin for Visual Studio 2012.

Slide1

Step 2 – Create a development site

I like to create Host Named Site Collections using the PowerShell command below. Specifiying the template to be “DEV#0? gives you an App Development optimized template.

New-SPSite “http://apptastic.is-sp2013.local” -HostHeaderWebApplication “http://dev” -Name “Developer Site” -Description “SharePoint App Developer Site” -OwnerAlias “is-sp2013administrator” -language 1033 -Template “DEV#0?

Slide2

Step 3 – Create a SharePoint App Project

  1. Click Add > New Project > App for SharePoint 2013
  2. Enter Name and choose location > OK

Slide3

Step 4 – Specify the App Settings

  1. Choose (and validate) our development site for debugging
  2. Select “SharePoint-hosted” as way to host your App

Slide4

Step 5 – Add TypeScript Targets (1/2)

TypeScript by default will compile into ordinary JavaScript. However, to make that happen you need to update your project’s .csproj file.

  1. Right click on your project node in the solution explorer
  2. Click to unload the project
  3. Click Edit .csproj

Slide5

Step 6 – Add TypeScripts Targets (2/2)

Scroll to the bottom of the project’s .csproj file and just below the SharePoint targets, add the following xml snippet, save your changes and reload the project.

<PropertyGroup Condition=”‘$(Configuration)’ == ‘Debug'”>
<TypeScriptTarget>ES3</TypeScriptTarget>
<TypeScriptIncludeComments>true</TypeScriptIncludeComments>
<TypeScriptSourceMap>true</TypeScriptSourceMap>
<TypeScriptModuleKind>AMD</TypeScriptModuleKind>
</PropertyGroup>
<PropertyGroup Condition=”‘$(Configuration)’ == ‘Release'”>
<TypeScriptTarget>ES3</TypeScriptTarget>
<TypeScriptIncludeComments>false</TypeScriptIncludeComments>
<TypeScriptSourceMap>false</TypeScriptSourceMap>
<TypeScriptModuleKind>AMD</TypeScriptModuleKind>
</PropertyGroup>
<Import Project=”$(VSToolsPath)TypeScriptMicrosoft.TypeScript.targets” />

Slide6

Step 7 – Download Require.js

To be able to create TypeScript modules (in separate files) you need a framework like CommonJS or require.js. Simply head over to http://requirejs.org/docs/download.html and download the latest version. Alternatively you can also get require.js using the NuGet Package Manager.

Slide7

Step 8 – Clean up the Scripts Directory

This is just a matter of personal taste, but I like to put .js files in folders for the sake of keeping on top of things.

Step 9 – Add a TypeScript file: App.ts

  1. Since we want to write our App purely using TypeScript we first should delete the default App.js file.
  2. Now right-click on the Scripts-Folder and add a new TypeScript file and enter App.ts as file name

Slide8

Step 10 – Add the generated .js file to your project

Since the latest release 9.1.1 the auto-generated .js file (which is the compiled JavaScript version of our TypeScript file) is no longer automatically added below our .ts file. Hence Visual Studio is not aware it’s around and for that reason it doesn’t end up in the App Package. There are probably better ways to solve this shortcoming, but I simply like to add the auto-generated .js files to my project.

Step 11 – Update the references in the App’s default page

So far we’ve update our project folder structure and added require.js. Hence we need to update the references in Default.apsx as follows:

  1. Update the path to jQuery (I moved it into the /Scripts/Vendor folder)
  2. Update the reference to App.js since it should be called via require.js and not directly (or else the require.js framework cannot perform its importing-tasks) as follows: <script type=”text/javascript” src=”../Scripts/vendor/require.js” data-main=”../Scripts/App”></script>

Slide9

Step 12 – Add TypeScript Definitions for jQuery and SharePoint using NuGet

We’re almost done with our preparations. We only need to get the TypeScript definition files for SharePoint and jQuery using NuGet. Simply open the NuGet Package Manager and search for “TypeScript SharePoint” and you’ll find both .d.ts files:

  1. jQuery.TypeScript.DefinitelyTyped
  2. SharePoint.TypeScript.DefinitelyTyped

Ps, a big Thank You to Boris Yankov: https://github.com/borisyankov/DefinitelyTyped

Slide10

In the next post I’ll be creating a simple App that will call a REST service …

Uncategorized

Nov.09

Consuming an External OData service from an SharePoint 2013 App

Ever wondered how you can consume an external OData Service from a SharePoint 2013 App? Well, I did and it came at the cost of a fair bit of headache because I kept feeling I was missing one essential piece of the puzzle: An ODataConnectionSettingId.

Ok, let’s start at the beginning. I created an account with visualstudio.com and wanted to retrieve data from my online TFS project in SharePoint. A quick google search made it obvious that, since visualstudio.com is offering an OData endpoint, I could simply create a SharePoint 2013 App with an External ContentType that would be the basis for a so called External List. Pretty simple! But then I got stuck at trying to configure my App to use a SharePoint Secure Store Application for storing my (Basic Authentication) Credentials …

Let’s go through it step by step. Prequisites for following my steps in your environment are a fully functional local SharePoint 2013 development enviroment with a working Business Connectivity Service Application and a Secure Store Service Application. And of course you’ll need an account for visualstudio.com.

Step 1 – Create a Develoment Site

I like to create Host Named Site Collections using the PowerShell command below. Specifiying the template to be “DEV#0″ gives you an App Development optimized template.

New-SPSite “http://apptastic.is-sp2013.local” -HostHeaderWebApplication “http://dev” -Name “Developer Site” -Description “SharePoint App Developer Site” -OwnerAlias “is-sp2013administrator” -language 1033 -Template “DEV#0″

Slide12

Step 2 – Create a SharePoint App Project in VS2012

  1. Click Add > New Project > App for SharePoint 2013
  2. Enter Name and choose location > OK

Slide13

Step 3 – Specify the App Settings

  1. Choose (and validate) our development site for debugging
  2. Select “SharePoint-hosted” as way to host your App

Slide14

Step 4 – Add ContentType for External Datasource

  1. Right click on the project node in the solution explorer
  2. Click Add
  3. Select Content Types for External Data Sources

Slide15

Step 5 – Specify the OData Source Settings

  1. Enter the OData Endpoint (in my case: https://tfsodata.visualstudio.com/DefaultCollection)
  2. Enter a name for your OData Source

Slide16

Step 6 – Select the Data Entities

  1. Select OData Entities (I selected WorkItems)
  2. Ensure that the option to create an External List is checked

Slide17

Step 7 – Create a Secure Store Application

Now we’ll leave our Visual Studio Project for a short moment to create a Secure Store Application where we’ll store the credentials for connecting to our External OData Source.

  1. Navigate to SharePoint 2013’s Central Administration > Application Management > Service Application > Secure Store Service
  2. In the Ribbon, click New
  3. Enter a Target Application ID which you can choose freely – Later it will be the unique identifier for BCS to locate your Application (with the credentials) in the Secure Store Service
  4. Enter your email address (as administrator)
  5. Select Group as Application Type

Slide18

Step 8 – Specify Field Types (User Name and Password) for Secure Store Application

What now follows may differ, depending on the type of authentication your OData Service requires. In my case, visualstudio.com’s OData Endpoint can be configured for Basic Authentication. Hence I needed to change the Field Types from Windows User Name and Windows Password to User Name and Password. Also, you’re only defining the Field Types (and hence the type of supported authentication). You’re NOT setting the credentials at this stage yet!

Slide19

Step 9 – Add Members that may access the Secure Store Application

On the next page you can enter Users and / or Groups that you want to grant access to the credentials stored in the Secure Store Application. In this case I entered “Everyone”. This means that everyone who uses the App that in turn will use this Secure Store Application will be permitted to make calls to the OData Service specified by the External ContentType.

Slide20

Step 10 – Set Credentials for Secure Store Application

We’re almost done. The Secure Store Application is created. Now select it and click Set Permissions in the Ribbon of the Secure Store Service and enter the credentials to access the OData Service Endpoint.

Slide21

Step 11 – Create a BCS Connection using PowerShell

This step is, as I mentioned before, one that I found is hardly documented. But it’s essential! Using PowerShell you need to create a so called ODataConnectionSettings that is then used by our External ContentType to establish a connection to our Secure Store Application. You can choose any name you’ll like. In the next step, however, we’ll need it to configure our External ContentType as it is the ODataConnectionSettingsId. The rest of the arguments for its parameter should be self-explanatory.

New-SPODataConnectionSetting -Name “TfsODataOnlineConnection” -ServiceContext “http://apptast
ic.is-sp2013.local” -ServiceAddressURL “https://tfsodata.visualstudio.com/DefaultCollection” -AuthenticationMode “Creden
tials” -SecureStoreTargetApplicationId “TfsODataOnline”

Slide22

Step 12 – Update External ContentType properties in your VS2012 Project

Now we’re ready to get back to our Visual Studio project. Update the following fields:

ODataConnectionSettingsId Use the name you specified in the previous step when you create the ODataConnectionsSettings using Powershell

AuthenticationMode For Basic Authentication you should select “Credentials”

SsoProviderImpementation Here we need to enter the Fully Qualified Assembly Name of SharePoint’s Secure Store Service:

Microsoft.Office.SecureStoreService.Server.SecureStoreProvider, Microsoft.Office.SecureStoreService, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c

Step 13 – Grant BCS Permissions for your App in the AppManifest

As a last step we need to update the AppManifest and make our App request permission to Read from SharePoint’s Business Connectivity Service (BCS).

Slide23

Step 14 – Update the Start Page of the App so your External List is loaded immediately

Slide24

Step 15 – Debug your App and Trust it

Hit F5, wait a short moment … Now check the box for the External Connection that the App is requesting permission to read from.

Slide25

Step 16 – Sit back and enjoy …

Slide26

Uncategorized

Jun.12

List does not exist The page you selected contains a list that does not exist. It may have been deleted by another user.

Are you a SharePoint developer and currently struggling with a SharePoint error, telling you “List does not exist The page you selected contains a list that does not exist. It may have been deleted by another user”? I struggled a long time with this error and none of the entries in Blog Posts that I found were worth a dime. What happened to me was that I was developing a SharePoint 2013 customization that upon Feature Activation creates a bunch of SharePoint Artefacts. Among those also a couple of lists. These lists, when called from the quick launch menu, are loaded by their Url. When I would then click the ribbon to load the List Settings the error occurred. After some time it daunted to me that while the list is loaded by its Url, the List Settings page is called by referencing the List ID e.g. “/_layouts/15/listedit.aspx?List=%7B98B7232C%2D8675%2D4FDB%2DB238%2DAB34D398A95D%7D”. Whilst I was testing a timer job I had briefly changed the date of my server to a future date and as of from that date the browser found a newer entry for a given page in its cache. So the solution was simple: Empty the browser cache (or never change the date of your development environment).

Uncategorized

May.04

So what are SharePoint Apps?

What should have a fun night between friends, ended in a long and weary discussion about SharePoint 2013 Apps, Multi-Tenancy and what it all means. And to be honest, I’m still not sure whether I hold the right end of the stick. So feel free and leave any comments helping me better understand everything there is about SharePoint 2013 Apps and what they really are.

I guess (one of) the shortest answers to my question “So what is a SharePoint 2013 App?” would be something like: “A website or service that can be found by navigating to a sub website of a specific SharePoint Tenant (called the App Tenant) in your current SharePoint Tenancy”. But I’m not sure whether anyone would understand that. So let’s first try and understand what an App Tenant is. For this you need to know that a Tenant is a Site Collection in a SharePoint Farm that has been added to set of Site Collections, known as a Tenancy. And maybe at this point it also would make sense to toss in the term Multi-Tenancy that refers to the situation in which more than one Tenancy can be configured within one and the same SharePoint Farm. The beauty of Multi-Tenancy is that each Tenancy is a fully isolated set of Site Collections. Fully isolated means that each Tenancy has its own unique user base, its own unique data (but on a Content Database/Tenancy level but much more granular permitting the allocation of Site Collections to Tenancies) and its own set of unique data stored in each of the SharePoint Service Applications (that require data uniqueness in the first place). Let’s assume for now that I have not lost you.

So let’s stop and review my definition of an App briefly against the backdrop of the previous paragraph: A website or service that can be found by navigating to a sub website of a specific SharePoint Tenant (called the App Tenant) in your current SharePoint Tenancy. So does this mean that in a SharePoint 2013 (on-premise) Farm you will find a Site Collection that is a Tenant specifically for Apps? No. To understand this, we need to explore two concepts: Firstly we need to understand how SharePoint isolates data between Tenancies. Secondly, we should at least understand at a very high level how browser requests are routed to Tenants (Site Collections) in a Tenancy in a SharePoint Farm.

Ok, let’s start with a quick look at how each Tenant is registered with a Tenancy in SharePoint. This is in fact, from a a conceptual point of view, nothing really special. It’s done using the Site and Subscription Service. Think of this as some kind of labeling machine putting Tenancy specific labels on all kind of data stored in SharePoint (e.g. Site Collections in Content Databases, data in Service Application Databases, users etc.). So basically you’d start by creating a new Tenancy in your SharePoint using this very service.

Now let’s see how it’s possible to access a subset of Site Collections (Tenants) using a browser? Well, at least not by using a Web Application with a Host Header. Why not? Because this wouldn’t scale and it’s difficult to balance load. If you consider Office 365 to probably be a very large Multi-Tenancy example then it’s easy to see that creating a Web Application on a per Customer basis is very inefficient. Also the usage of Managed Paths isn’t scalable enough (due to the restricted number you can create per Web Application). And besides, it’s also insecure. Because all different Tenancies would basically be created within a single domain e.g. http://office365.microsoft.com/tenancy1, http://office365.microsoft.com/tenancy2 etc. and this would allow all kinds of cross-site scripting vulnerabilities. No, it seems to be much more efficient and secure to use Host Named Site Collections e.g. http://tenancy1.office365.microsoft.com, http://tenancy2.office365.microsoft.com etc. But wait a minute: That would reduce the number of Site Collections per Tenancy to exactly one, wouldn’t it? Because if http://tenancy1.office365.microsoft.com is a Tenant (Site Collection), it would you from creating a normal Managed Path like http://tenancy1.office365.microsoft.com/sites and then add additional Site Collections “below” the Managed Path, e.g. http://tenancy1.office365.microsoft.com/sites/sc1, http://tenancy1.office365.microsoft.com/sites/sc2, right? Wrong! New with SharePoint 2013 you can create so-called Host Named Managed Paths that do exactly that. They help virtually grouping Site Collections within or below) a single (sub) domain. Having all this in place, it’s easy to see that this would scale virtually unlimited by adding as many Web Front Servers to handle the load and as many Content Databases needed to hold all the data in all the Site Collections that make up for all the Tenancies in the Farm.

Ok, so what is the App Tenant if it’s not simply another Site Collection that has been registered with a SharePoint Tenancy? Well, exactly that! It’s a Tenant because it’s registered as such in the Site and Subscription Settings Service and it has it’s own path URL browsers to navigate to. However, the path is not pointing to the root website of a Site Collection because it was never created. It’s basically pointing into the void! But as soon as you’ll install and deploy your App, SharePoint will take the App Tenant’s URL and dynamically pre-fix it (actually, create a sub domain for it) with some abbreviations and unreadable GUID. So whatever an App is: SharePoint has made sure it will always have a URL and restricted the availability of the App to a single SharePoint Tenancy .

So what’s an App? An App is a website or web service that seems to run as if it were installed as a sub website of the App Tenant in your current Tenancy. But in reality, behind the scenes there is a lot of http re-directing, permission checking and request interception going on. Eventually requests to your App are redirected to the actual location of the website or web service that makes up for the technical implementation of your App. And here you have basically three choices: 1) a client- and serverside software that running in some cloud; either a) azure or b) some other 3rd party cloud) or 2) clientside software that runs somewhere in SharePoint. SharePoint is “simply” using its Tenancy-Infrastructure (and additional App Management functionality) on the one hand side to control (but also to ensure) that the App can access Tenancy specific data e.g. Users, Documents, List Items etc. (according to permissions granted during App deployment). On the other hand side SharePoint uses this infrastructure to know the URL of the App. And hence it can intercept all request to the App, which allows SharePoint to apply a comprehensive permission model.

Probably at this point you are wondering when you ever explicitely enabled (Multi-)Tenancy whilst installing SharePoint 2013 on-premise. Most likely you didn’t! This is because a SharePoint Tenancy is automatically created and configured for you by default if you didn’t configure Multi-Tenancy yourself. In fact, SharePoint created a so called Default Tenancy for you. And without you realizing, you’ve been adding new Tenants (Site Collections) to your (Default) Tenancy all along. Why? Because without it, you wouldn’t have been able to create the App Tenant. So without the Default Tenancy automatically configured for you, you wouldn’t be able to host Apps in your SharePoint 2013 environment; Ever! So fact is, if you like it or not, your SharePoint 2013 on-premise installation is a Tenancy. However, this doesn’t mean that you now can add more Tenancies to your Farm. In other words, Multi-Tenancy would still require manual configuration (see the last paragraph for some thoughts on this).

Uncategorized

Mar.23

Consume your custom (SharePoint 2013 hosted) WCF Data Service in a SharePoint 2013 App

I think this will be my shortest post ever. To consume your custom (SharePoint 2013 hosted) WCF Data Service in a SharePoint 2013 App you can simply follow the instructions you find here: http://msdn.microsoft.com/en-us/library/jj163088.aspx.

Then why is this still worth a post? Simply because I think that it’s very easy to oversee this option and start digging in the wrong direction. In my case I believed that because a SharePoint hosted WCF Data Service is basically available at

hostweburl + '/_vti_bin/your_data_service_folder/your_data_service.svc'

It would be also accessible. But I found that available isn’t the equivalent of accessible. I’m still not sure whether I’m facing a flaw in my SharePoint 2013 environment or whether it really is not possible to go beyond the boundaries of the host web using SP.RequestExecutor or AppContextSite (also see my question here: http://social.msdn.microsoft.com/Forums/en-US/appsforsharepoint/thread/92242740-76bc-4f43-97e7-9cba348d2770/#f58df6e4-f73d-400c-8750-0dc05a3f6f7b). But using External ContentTypes for Apps I found a quick and easy way to consume my custom (SharePoint 2013 hosted) WCF Data Service afterall.

One remark

To make things work for a WCF Data Service you may need to change the following line in the sample code

"accept": "application/json",

to

"accept": "application/json; odata=verbose",
Uncategorized

Mar.23

Hosting a WCF Data Service in SharePoint (2013)

Hosting a WCF Data Service in SharePoint requires you to make a few important changes or else it will fail.

1. Change the servicehost Factory

If you created your WCF Data Service using VS2012 you will find that the servicehost Factory is set to

System.Data.Services.DataServiceHostFactory, Microsoft.Data.Services, Version=5.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

However, for WCF services hosted in SharePoint you can use other Factories that will take the burden of configuring endpoints, bindings and behaviors of your shoulders (http://msdn.microsoft.com/en-us/library/ff521586(v=office.14).aspx). Even though this information applies to SharePoint 2010, it’s still valid for 2013. So I ended up changing my Factory to

Microsoft.SharePoint.Client.Services.MultipleBaseAddressDataServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c

2. Disable Impersonation

If you can access the service but none of it’s methods because ‘NT AUTHORITYIUSR’ cannot logon, changes are that you need to update the Web.Config so that impersonation is set to false. That way IIS won’t use the IUSR account for impersonation.

<system.web>...
<identity impersonate="false">
</system.web>

Additional configurations

3. Add the connection string to the Web.Config (if you’re using Entity Framework)

If you make use of an Entity Framework you need to add the Entity connection string to your web service Web.Config file

4. Deploy Microsoft.Data.Services and dependencies to GAC

I had a couple of “misunderstandings” with NuGet as well as with the way the WCD Data Service packages are updated and deployed to GAC. In my case I ended up including the correct versions in my SharePoint WSP package for GAC deployment. But in reality, this simply should be a prerequisite before the application is deployed. The caveat with having the wrong versions referenced is that you may see errors indicating that the service type was not found. But reality a wrong version of Microsoft.Data.Services was loaded (5.0.0.0 instead of 5.3.0.0).

Some final thoughts …

Obviously it doesn’t make much sense in the SharePoint 2013 age to deploy web services to SharePoint’s ISAPI folder. The lower the number of dependencies on the base installation of SharePoint, the easier it is to upgrade to the next version. So of course you should create a remote app. But this would immediately increases the complexity as you need to implement proper authentication (and possibly authorization).

Uncategorized