Consuming a WCF service from SharePoint 2010 Sandbox Solutions

This article would focus on how to consume a WCF Service from a SharePoint 2010 Sandbox Solutions. We’ll start with a quick briefing of what are the limitations of  SharePoint 2010 Sandbox Solutions and What is a SharePoint 2010 Full trust proxy, before jumping on to the actual steps for consuming a WCF service from SharePoint 2010. We all know that a SharePoint 2010 Sandbox Solutions get executed inside a controlled environment (runs inside spucworkerprocess.exe) and it also has restrictions in terms of both internal restrictions and external restrictions.

                                                                                                                      The internal restrictions of SharePoint 2010 Sandbox Solutions means only a subsite of functionalities inside Microsoft.SharePoint.dll is available.  The external restrictions of SharePoint 2010 Sandbox Solution means only it can call assemblies which allows partially trusted callers, hence a sub-set (about one third) of .NET framework assemblies  is only available to SharePoint 2010 Sandbox Solutions. On top of this there are many more external restrictions like accessing file-system, web services / wcf services, databases etc. , a complete list of restrictions are available in msdn documentation.

The Full-Trust Proxy comes into picture when we want to reach the external world from the SharePoint 2010 Sandbox Solutions. The Full-Trust proxy runs inside a separate process called spucworkerprocessproxy.exe, which allows partially trusted callers.

I’ve the following WCF service hosted in IIS, which needs to be accessed from SharePoint 2010 Sandbox Visual Web Part. For the sake of the demonstration I’ve kept the service functionality simple, it takes an integer as a parameter and returns a hardcoded string. The following is the service method implementation.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace CreditCardService
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "VadlidationService" in code, svc and config file together.
    public class VadlidationService : IVadlidationService
    {
        public string GetCountry(int value)
        {
            return "India";
        }

        
    }
}
The service contract of the service looks like the following 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace CreditCardService
{
    [ServiceContract]
    public interface IVadlidationService
    {

        [OperationContract]
        string GetCountry(int value);

    

        // TODO: Add your service operations here
    }


    
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Now let’s move to the development of full-trust proxy. To develop a full-trust proxy assembly, two component needs to be developed one is full-trust proxy  operation class and the other is full-trust proxy arguments class.

The full-trust proxy operation should be inherit from spproxyoperation class and it should be marked with [Serializable] attribute.  Then the required set of properties need to be implemented.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.UserCode;

namespace SimpleProxy
{
    [Serializable]
    [Microsoft.SharePoint.Security.SharePointPermission(System.Security.Permissions.SecurityAction.LinkDemand, ObjectModel = true)] 

   public class SimpleProxyArgs : SPProxyOperationArgs
    {

      public int CountryId { get; set; }

       public static string ProxyOperationTypeName
       {

           get
           {
               return "SimpleProxy.SimpleProxyOps";
           }
       }


       public static string ProxyAssemblyName

       {

           get
           {
               return "SimpleProxy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a3daa9085766b846"; 
                       
           
           }
       
       
       
       }

    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The property CountryId would be used by the full-trust proxy operation class to pass the arguments to the WCF service.  The two additional properties like

ProxyOperationType and ProxyAssemblyName is defined here and it would be used by the Sandbox Solution to get the assembly details while making the calls

to full-trust proxy components.

The full-trust proxy operation class should inherit from spproxyoperation class, override the Execute method to implement all the required full-trust logic.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.UserCode;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace SimpleProxy

{

    [Microsoft.SharePoint.Security.SharePointPermission(System.Security.Permissions.SecurityAction.LinkDemand, ObjectModel = true)] 

    public class SimpleProxyOps : SPProxyOperation
    {
        
        
             
        public override object Execute(SPProxyOperationArgs args)
        {
            WSHttpBinding oWSHttpBinding = new WSHttpBinding(SecurityMode.Message);


            oWSHttpBinding.Name = "WSHttpBinding_IVadlidationService";

            oWSHttpBinding.CloseTimeout = new TimeSpan(0, 1, 0);
            oWSHttpBinding.OpenTimeout = new TimeSpan(0, 1, 0);
            oWSHttpBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
            oWSHttpBinding.SendTimeout = new TimeSpan(0, 1, 0);
            oWSHttpBinding.BypassProxyOnLocal = false;
            oWSHttpBinding.TransactionFlow = false;
            oWSHttpBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;


            oWSHttpBinding.MaxBufferPoolSize = 524288;
            oWSHttpBinding.MaxReceivedMessageSize = 65536;
            oWSHttpBinding.MessageEncoding = WSMessageEncoding.Text;
            oWSHttpBinding.TextEncoding = Encoding.UTF8;
            oWSHttpBinding.UseDefaultWebProxy = true;
            oWSHttpBinding.AllowCookies = false;


            oWSHttpBinding.ReaderQuotas.MaxDepth = 32;
            oWSHttpBinding.ReaderQuotas.MaxStringContentLength = 8192;
            oWSHttpBinding.ReaderQuotas.MaxArrayLength = 16384;
            oWSHttpBinding.ReaderQuotas.MaxBytesPerRead = 4096;
            oWSHttpBinding.ReaderQuotas.MaxNameTableCharCount = 16384;

            oWSHttpBinding.ReliableSession.Ordered = true;
            oWSHttpBinding.ReliableSession.InactivityTimeout = new TimeSpan(0, 10, 0);
            oWSHttpBinding.ReliableSession.Enabled = false;

            oWSHttpBinding.Security.Mode = SecurityMode.Message;

            oWSHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
            oWSHttpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
            oWSHttpBinding.Security.Transport.Realm = "";

            oWSHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
            oWSHttpBinding.Security.Message.NegotiateServiceCredential = true;


            EndpointAddress oEndPointAddress = new EndpointAddress(new Uri("http://lt012464.abc.com:1414/CreditCardService/ValidationService.svc"));

            
            
            var proxyArgs = args as SimpleProxyArgs;

            int CountryId = proxyArgs.CountryId;


            SimpleProxy.VadlidationServiceClient ovsc = new SimpleProxy.VadlidationServiceClient(oWSHttpBinding, oEndPointAddress);
                
            string result = ovsc.GetCountry(0);
            

            return result;

        }

    }
}

I’ve overridden the execute method and now I need to access the WCF service inside execute method. There are several ways of accessing a WCF service from a .NET assembly. If you create the client proxy for the WCF service using ‘Add Service Reference’ option in Visual Studio 2010, it would be difficult to configure and set the endpoint inside full-trust proxy class. Because full-trust proxy runs inside spucworkerprocesproxy.exe, which cannot access the WCF client endpoint configuration defined in the web.config. The only viable option to set the endpoint inside full-trust proxy component is to create the endpoint through code (programmatically).

Then, add this attribute [assembly: AllowPartiallyTrustedCallers] to the assemblyinfo.cs file  to allow the partially trusted callers (Sandbox Solutions) to invoke it.

The next logical step is to register the full-trust proxy assemblies to the User Code service of Sandbox infrastructure. Preferably, this action should be performed inside the feature activation of the full-trust proxy solution.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {

            SPUserCodeService userCodeService = SPUserCodeService.Local;
            var simpleOperation = new SPProxyOperationType(SimpleProxyArgs.ProxyAssemblyName,SimpleProxyArgs.ProxyOperationTypeName);
            userCodeService.ProxyOperationTypes.Add(simpleOperation);
            userCodeService.Update();
            
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Now we are all set to invoke this full-trust proxy from a SharePoint 2010 Sandboxed Visual Web Part. Invoke the SPUtility.ExecuteRegisteredProxyOperation method from the sandboxed visual webpart to make this happen.

namespace SharePointSaturdayWebPart.SharePointSaturdayWebPart
{
    [ToolboxItem(false)]
    public partial class SharePointSaturdayWebPart : System.Web.UI.WebControls.WebParts.WebPart
    {
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            InitializeControl();
        }

        protected void Page_Load(object sender, EventArgs e)
        {

            var proxyArgs = new SimpleProxyArgs();
            proxyArgs.CountryId = 1;



                var result = SPUtility.ExecuteRegisteredProxyOperation(SimpleProxyArgs.ProxyAssemblyName, 
                                                                        SimpleProxyArgs.ProxyOperationTypeName, proxyArgs);





                Label1.Text = (string)result;




        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

 Subscribe to my blog