Nov 182011
 

This is the fourth post in a series of writing a SharePoint Service Application. As stated in part 3, if you’ve missed the first posts, read those or this won’t make a lick of sense. (It may not make a lick of sense regardless!) Once again, note that creating a service application is OVERKILL for most things you’ll create for SharePoint. And while this example only spits out a simple “Hello World” type of sample, you can use this as “starter code” for a much more complex application.

For those who attended SharePoint Saturday in Denver last week, here’s the promised code, and thanks for coming to my session!

Here are the first four posts in this series:

Now that you have the guts in place, it’s time to create the deployment solution. Whenever you write complex SharePoint solutions, it’s best to keep the SharePoint project that creates the WSP file scoped to the deployment. Think of it ONLY as the Installer project for your SharePoint solution.

Since it’s been a while since I wrote the first posts (I am sorry if you’ve been waiting…), I am going to take the lazy approach here, and post the code for you to download and follow along. Rather than trying to walk you through creating it from scratch, simply download the source and I’ll talk you through the components.

DOWNLOAD THE SERVICE APPLICATION SOURCE HERE

In the source code you’ll see 2 project: the first is the SuperCoolServiceLibrary. This is the code for the service application, as discussed in the previous posts. The second is the SharePoint “installer” project which we’ll focus on in this post.

The WSP needs to contain the following mapped folder structure:

  • ADMIN (mapped folder {SharePointRoot}TemplateADMIN)
    • SuperCoolServiceApplication
      • Default.aspx
      • Manage.aspx
  • Layouts (mapped folder {SharePointRoot}TemplateLayouts)
    • SuperCoolServiceApplication
      • Debug.aspx (just a test page)
  • WebClients (mapped folder {SharePointRoot}WebClients)
    • SuperCoolServiceApplication
      • Client.config
  • WebServices (mapped folder {SharePointRoot}WebServices)
    • SuperCoolServiceApplication
      • Web.config
      • SuperCool.svc

 

Let’s start with the most obvious. WebServices contains the WCF web service which will be the backend WCF service. Note that it needs a Web.config file as well as the .svc file.

Next up is WebClients. WebClients contains the client.config file which the configuration based channel factory will use to communicate to the backend service.

Then… the Layouts folder contains just a debug page which will exercise the Hello World method in the service.

Most importantly, you’ll need to include an installer feature which will create the instances and make available your service in the Manage Application Services page. That code looks like this (from installer.eventreceiver.cs):

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPFarm farm = SPFarm.Local;
    SPServer server = SPServer.Local;

    // check if the service has already been installed in the farm
    var service = farm.Services.GetValue<SuperCoolService>();
    if (service == null)
    {
        // create the service
        service = new SuperCoolService(farm);
        service.Status = SPObjectStatus.Online;
        service.Update();
    }

    // with the service added to the farm, see if there is a installed instance on the server... 
    //   if not, create it
    SuperCoolServiceInstance serverSvcInstance = new SuperCoolServiceInstance(server, service);
    serverSvcInstance.Update(true);

    // install the service proxy into the farm if isn't already installed
    var serviceProxy = farm.ServiceProxies.GetValue<SuperCoolServiceProxy>();
    if (serviceProxy == null)
    {
        serviceProxy = new SuperCoolServiceProxy(farm);
        serviceProxy.Update(true);
    }
}

        
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
    SPFarm farm = SPFarm.Local;
    SPServer server = SPServer.Local;

    //Remove all instances
    var instance = server.ServiceInstances.GetValue<SuperCoolServiceInstance>();
    while (instance != null)
    {
        server.ServiceInstances.Remove(instance.Id);
        instance = server.ServiceInstances.GetValue<SuperCoolServiceInstance>();
    }
//Uninstall the service proxy var serviceProxy = farm.ServiceProxies.GetValue<SuperCoolServiceProxy>(); if (serviceProxy != null) { farm.ServiceProxies.Remove(serviceProxy.Id); } //Remove service and jobs var service = farm.Services.GetValue<SuperCoolService>(); if (service != null) farm.Services.Remove(service.Id); }

In the Admin folder, you’ll need a default.aspx page which will be the UI to CREATE an instance of your service application. You can also include an admin page here as well.

The code behind from the default page in Admin contains the following code, which is used to create your service application:

var service = SPFarm.Local.Services.GetValue<SuperCoolService>();
if (service == null)
    throw new InvalidOperationException("The service is not installed.");
                    
//// Get the service proxy object
SuperCoolServiceProxy serviceProxy = SPFarm.Local.ServiceProxies.GetValue<SuperCoolServiceProxy>();
if (null == serviceProxy)
    throw new InvalidOperationException("The service proxy is not installed.");
                    
var appPoolInput = this.applicationPoolSection as IisWebServiceApplicationPoolSection;
using (SPLongOperation operation = new SPLongOperation(this))
{
    operation.Begin();

    SPIisWebServiceApplicationPool pool = null;
    pool = appPoolInput.GetOrCreateApplicationPool();
    if (pool == null)
        throw new ConfigurationErrorsException("Could not find an application pool to use!!!");

    var app = new SuperCool.ServiceLibrary.SuperCoolServiceApplication( 
            this.serviceNameInput.Text, service, pool);

    app.Update(true);

    app.Provision();

    var serviceAppProxy = new SuperCoolServiceApplicationProxy("Super Cool Service Proxy Thingamajig",
        serviceProxy, app.Uri);
    serviceAppProxy.Provision();
    SPServiceApplicationProxyGroup.Default.Add(serviceAppProxy);
    SPServiceApplicationProxyGroup.Default.Update();

    //Start the service instance
    var server = SPServer.Local;
    var instance = server.ServiceInstances.GetValue<SuperCoolServiceInstance>();
    if (instance == null) 
    {   //It should be created during feature activation, but if we can't find it  - create it here.
        instance = new SuperCoolServiceInstance(server, service);
        instance.Update(true);
    }
    if (instance != null && instance.Status != SPObjectStatus.Online)
        instance.Provision();
}
this.SendResponseForPopUI();

In case you missed the link, DOWNLOAD THE SERVICE APPLICATION SOURCE HERE. Then you can play along!!! I’ll post more notes on this in the coming weeks after the Thanksgiving celebrations die down.

To all of you, Happy Thanksgiving, and may you have a great season of the holy days.

Daniel Larson

Jan 042011
 

This is the third post in a series of writing a SharePoint Service Application. If you’ve missed the first posts, read those or this won’t make a lick of sense. (It may not make a lick of sense regardless!) Also note that creating a service application is OVERKILL for most things you’ll create for SharePoint… this post is NOT “how to write WCF code for SharePoint”, it is VERY SPECIFIC to creating a SharePoint 2010 Managed Service Application. And while this example only spits out a simple “Hello World” sample, you can use this as “starter code” for a much more complex application.

So far, we’ve created the service application library, but no WCF service. In this post, we’ll create the WCF service and a WCF client to call it using the SharePoint application proxy. The WCF client will run on the web front end and will call back to the application server’s instance of the application through your backend web service. In the next post, we’ll cover how to actually deploy the service application in the WSP project. But for now, we’ll just look at the code side of the WCF library. Because we really want to focus on CODE, we won’t cover the WCF configuration stuff quite yet. We’ll take care of that in the WSP project.

The Service Interface

As with any WCF service, start with the service interface. Ours will simply return a debug statement—which is actually ideal to include in your production WCF service for debugging support issues. Here is my service interface:

using System;
using System.ServiceModel;

namespace SuperCool.ServiceLibrary.Services
{
    /// <summary>
    /// Defines the WCF interface to the backend service application
    /// </summary>
    /// <remarks>Referenced in WebClients[app name]client.config</remarks>
    [ServiceContract]
    public interface ICoolService
    {
        [OperationContract]
        string GetServiceDebug();
    }
}

The Service Class

For our service implementation, I’ll return some debug info about the call, so you can see what URL it actually gets called on and what identity is calling it. You can also include the attribute [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.NotAllowed)] on this class, since we do NOT support this interface in an ASP.NET context.

using System;
using Microsoft.SharePoint.Administration.Claims;
using System.ServiceModel;
using System.Globalization;
using Microsoft.SharePoint.Administration;

namespace SuperCool.ServiceLibrary.Services
{
    /// <summary>
    /// The actual WCF-callable backend service. (The rest is plumbing)
    /// </summary>
    public class CoolService : ICoolService
    {
        // this method would be called as the user.
        public string GetServiceDebug()
        {
            var encAccountName = 
               SPClaimProviderManager.Local.GetUserIdentifierEncodedClaim(
               ServiceSecurityContext.Current.PrimaryIdentity);
            var claimsAccountName = 
               SPClaimProviderManager.Local.ConvertClaimToIdentifier(encAccountName);

            // you can also use System.Threading.Thread.CurrentPrincipal.Identity

            SPIisWebServiceApplication current = SPIisWebServiceApplication.Current;

            return string.Format(CultureInfo.InvariantCulture, 
                @"Hello, {0}. So glad you used ""{1}"" (a {2}, id {3}) at {4}.",
                claimsAccountName,
                current.DisplayName,
                current.GetType().Name,
                current.Id,
               OperationContext.Current.IncomingMessageProperties[
               "OriginalHttpRequestUri"]               
                );
        }
    }
}

 

This backend service will ALWAYS be authenticated by claims authentication—it is the delegation used by SharePoint, which we’ll configure in the client in the next code sample. The ServiceSecurityContext.Current.PrimaryIdentity will be a ClaimsIdentity, which you can get the current user from. To get the current User Profile from the backend service, you can use this claims identity to get the user profile from the User Profile application (it will use the User Profile Application that is in the same proxy group as your application).

private UserProfile GetDelegatedIdentityProfile()
{
   var encAccountName = 
      SPClaimProviderManager.Local.GetUserIdentifierEncodedClaim(
      ServiceSecurityContext.Current.PrimaryIdentity); 
   var claimsAccountName = 
      SPClaimProviderManager.Local.ConvertClaimToIdentifier(encAccountName);
   var um = new UserProfileManager(SPServiceContext.Current, false, false);
   return um.GetUserProfile(claimsAccountName, false);
} 

Getting the Service Application from your WCF Code

Within your service, there is a MAGIC property that you can use to get the instance of your application. SPIisWebServiceApplication.Current will return the current application that your service is created under. Note that you can create multiple instances of the service on the Central Administration “Manage Service Applications” page, each of which could have it’s own persisted data, database connections and more. This magic property is the only thing that ties your WCF class to your service application! In the smaple code above, you’ll see that we’re simply writing out the name and id of the application.

Calling the Backend through a Service Client

The last part of the plumbing is creating an actual service client. With “sane” WCF programming, you usually let Visual Studio do this type of thing for you. Not so in this case however, you’ve got to do a lot to the channel with credentials and dynamic, load-balanced endpoint URLs.

Most of this is “boilerplate” code that uses the SharePoint load balancer to call the application servers. To scale out your application, be sure the service instance is running on multiple app servers. You’ll need to start it on the “Services on Server” page (well, after you create the WCF project, which we’ll do in the next post!). The client will use the ConfigurationChannelFactory and the proxy’s configuration which we’ll deploy in the WSP project in order to configure itself. Specifically, the channel factory will call SuperCoolServiceApplicationProxy.Configuration to read the configuration for the client. We previously defined this as:

internal Configuration Configuration
{
    get
    {
        return OpenClientConfiguration(SPUtility.GetGenericSetupPath(
            @"WebClientsSuperCoolServiceApplication"));
    }
}

Following is a client class that contains the infrastructure to call the backend service. You could make this into a common base class, but I’ll keep it simple to get the concept across. The main methods to look at are “ExecuteOnChannel” and GetChannel. After that, just implement the interface of your service, and use ExecuteOnChannel to call the backend service.

using SuperCool.ServiceLibrary.Services;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System;

namespace SuperCool.ServiceLibrary.Client
{
   /// <summary>
   /// A WCF client for talking through the WCF channel to the backend. 
   /// </summary>
   public class SuperCoolClient : ICoolService
   {
      private SPServiceContext serviceContext;

      public SuperCoolClient() : this(null) { }
      public SuperCoolClient(SPServiceContext serviceContext)
      {
         if (serviceContext == null)
         {
            if (SPServiceContext.Current != null)
               serviceContext = SPServiceContext.Current;
            else
               this.serviceContext = SPServiceContext.GetContext(
                   SPServiceApplicationProxyGroup.Default,
                   new SPSiteSubscriptionIdentifier(Guid.Empty));
         }
         else
            this.serviceContext = serviceContext;

         if (this.serviceContext == null)
            throw new ArgumentNullException("serviceContext");
      }

      private delegate void CodeToExecuteOnChannel(ICoolService channel);

      private void ExecuteOnChannel(CodeToExecuteOnChannel codeBlock, 
         bool asProcess)
      {
         if (codeBlock == null)
            throw new ArgumentNullException("codeBlock");
         SuperCoolServiceApplicationProxy proxy = 
            (SuperCoolServiceApplicationProxy)
            this.serviceContext.GetDefaultProxy(
            typeof(SuperCoolServiceApplicationProxy));

         if (proxy == null)
            throw new InvalidOperationException("service proxy not found.");

         SPServiceLoadBalancer loadBalancer = proxy.LoadBalancer;
         if (loadBalancer == null)
            throw new InvalidOperationException("load balancer not found.");

         SPServiceLoadBalancerContext loadBalancerContext = 
            loadBalancer.BeginOperation();
         try
         {
            using (new SPServiceContextScope(this.serviceContext))
            {
               // Get a channel to the service application endpoint
               IChannel channel = (IChannel)GetChannel(proxy,
                   loadBalancerContext.EndpointAddress, asProcess);
               try
               {
                  // Execute the delegate
                  codeBlock((ICoolService)channel);

                  // Close the channel
                  channel.Close();
               }
               finally
               {
                  if (channel.State != CommunicationState.Closed)
                     channel.Abort();
               }
            }
         }
         catch (EndpointNotFoundException)
         {
            loadBalancerContext.Status = SPServiceLoadBalancerStatus.Failed;
            throw;
         }
         finally
         {
            loadBalancerContext.EndOperation();
         }
      }

      // Used to cache the client channel factory
      private static ChannelFactory<ICoolService> s_ChannelFactory;
      private static object s_ChannelFactoryLock = new object();

      private ICoolService GetChannel(SuperCoolServiceApplicationProxy proxy,
          Uri address, bool asProcess)
      {
         if (proxy == null)
            throw new ArgumentNullException("proxy");

         if (address == null)
            throw new ArgumentNullException("address");

         // Check for a cached channel factory
         if (s_ChannelFactory == null)
         {
            lock (s_ChannelFactoryLock)
            {
               if (s_ChannelFactory == null)
               {
                  s_ChannelFactory = 
                     new ConfigurationChannelFactory<ICoolService>(
                     "https", proxy.Configuration, null);

                  // Configure the channel factory for claims-based auth
                  s_ChannelFactory.ConfigureCredentials(
                     SPServiceAuthenticationMode.Claims);
               }
            }
         }
         if (asProcess)
            return s_ChannelFactory.CreateChannelAsProcess(
               new EndpointAddress(address));
         else
            return s_ChannelFactory.CreateChannelActingAsLoggedOnUser(
               new EndpointAddress(address));
      }

      public string GetServiceDebug()
      {
         string result = null;
         this.ExecuteOnChannel(
            channel => result = channel.GetServiceDebug(), asProcess: false);
         return result;
      }
   }
}

Also note that ExecuteOnChannel takes an “asProcess” parameter—we will either create the channel as the logged in user and configure the SharePoint claim to delegate, or we’ll delegate the current process identity.

The last piece is a ServiceHostFactory. There’s not too much to this, but you’ve got to use a custom host factory to configure claims authentication. We’ll reference this class in the .SVC file. Not sure why there isn’t one of these that SharePoint ships, or maybe there is one that I’m missing… but for some reason you have to do this to wire up the backend WCF endpoint. There’s nothing special about this class for our application.

using System;
using System.ServiceModel.Activation;
using System.ServiceModel;
using Microsoft.SharePoint;

namespace SuperCool.ServiceLibrary.Services
{
    internal sealed class SuperCoolHostFactory: ServiceHostFactory
    {
        public override ServiceHostBase CreateServiceHost(
           string constructorString, Uri[] baseAddresses)
        {
            ServiceHostBase serviceHost = base.CreateServiceHost(
               constructorString, baseAddresses);
            serviceHost.Configure(SPServiceAuthenticationMode.Claims);
            return serviceHost;
        }
    }
}

With the WCF interface, service and client in place, we’re now ready to deploy the service application in a WSP project. We’ll do that next… stay tuned! (And I promise, I’ll post the whole source code for this… )

Dec 162010
 

Read the previous posts in this series: Intro, Part 1

In the previous post, we looked at a simple SPIisWebServiceApplication. The SPIisWebServiceApplication is the “instance” of your application that gets provisioned and is managed in Central Administration. It “has” things like the database, jobs and persisted storage associated with it. Soon we’ll add a WCF service which web front end code (and remote farms!) can talk to it with. But before we’ll be able to create the application, we’ll need to define the “Create” links to it in the SharePoint infrastructure. We’ll need a service, a service instance, and then we’ll need a proxy and application proxy in order to talk to it. In this post we’ll focus on these components—they are simple “infrastructure” components without anything super-fancy.

The following class is used to create a menu-item for creating a new Service Application on the Manage Service Applications page. Really the only thing you need to edit is the GetApplicationTypeDescription method and the GetCreateApplicationLink method. The rest is “plumbing” code which you really don’t need to change.

Also note that each of these classes has a Guid attribute. Put a Guid attribute on each “Infrastructure” class for the service application. These generally aren’t referenced, but you’ll need to reference the SPIisWebServiceApplication’s GUID in it’s proxy class. 

In the service library project, the following SPIIsWebService class creates the link to creating a new Super Cool Service application. As I mentioned, there’s nothing spectacular about this code. It’s just used to create the link.

using System;
using Microsoft.SharePoint.Administration;
using System.Runtime.InteropServices;

namespace SuperCool.ServiceLibrary
{
    /// <summary>
    /// The service plumbing-- the thing that's creatable from Central Admin.
    /// </summary>
    [Guid("6810AFD9-3137-4bc2-8AF1-00EB8461A861")]// arbitrary GUID, not referenced in code. 
    public sealed class SuperCoolService : SPIisWebService, IServiceAdministration
    {
        public SuperCoolService() { }
        public SuperCoolService(SPFarm farm): base(farm) { }

        Type[] IServiceAdministration.GetApplicationTypes()
        {
            return new Type[] { typeof(SuperCoolServiceApplication) };
        }

        SPPersistedTypeDescription IServiceAdministration.GetApplicationTypeDescription(
Type serviceApplicationType) { if (serviceApplicationType != typeof(SuperCoolServiceApplication)) { throw new NotSupportedException(); } return new SPPersistedTypeDescription( "Super Cool Service", "A super cool service application for managing super cool things."); } public override SPAdministrationLink GetCreateApplicationLink(Type serviceApplicationType) { return new SPAdministrationLink("/_admin/SuperCoolServiceApplication/default.aspx"); } public override SPCreateApplicationOptions GetCreateApplicationOptions(
Type serviceApplicationType) { return SPCreateApplicationOptions.None; } SPServiceApplication IServiceAdministration.CreateApplication( string name, Type serviceApplicationType, SPServiceProvisioningContext provisioningContext) { throw new NotSupportedException(); } SPServiceApplicationProxy IServiceAdministration.CreateProxy( string name, SPServiceApplication serviceApplication, SPServiceProvisioningContext provisioningContext) { throw new NotSupportedException(); } } }

The next class you’ll need is the SPIisWebServiceInstance class. This isn’t really used for anything but letting SharePoint define which service runs on each server. Nothing spectacular here, and the only thing you really need to specify is the name.

using System.Runtime.InteropServices;
using Microsoft.SharePoint.Administration;

namespace SuperCool.ServiceLibrary
{
    [Guid("0EBCD546-373C-4c1e-83BD-1D3B504F3E98")]
    public class SuperCoolServiceInstance : SPIisWebServiceInstance
    {
        public SuperCoolServiceInstance() { }
        public SuperCoolServiceInstance(SPServer server, SuperCoolService service)
            : base(server, service) { }
                
        /// <summary>
        /// Gets the display name for the administrative UI.
        /// </summary>
        public override string TypeName
        {
            get { return "Super-Duper Super-Cool Service"; }
        }
    }
}

That’s it for Service classes. Now we’ll need to add Proxy classes, which are used by the front end to talk to the backend. These are relatively simple classes too, nothing but plumbing.

The SPIisWebServiceProxy is used to connect to the service application. This isn’t the ACTUAL application proxy, just the “glue” to tie the application proxy to the service. It needs bi-directional references with the SPIisWebServiceApplicationProxy, so if you’re writing your own create your own subclasses of SPIisWebServiceProxy and SPIisWebServiceApplicationProxy at this time. My classes will be called SuperCoolServiceProxy and SuperCoolServiceApplicationProxy.

First, the SPIisWebServiceProxy is used to connect the proxies to the service. The important thing here though is that you need to reference the service application’s Guid that this supports. This is the magic that lets it work… without matching Guids in the SupportedServiceApplication attribute it’s not going to work. The rest is self-explanatory, just copy the following code and refactor it for your app.

using System;
using Microsoft.SharePoint.Administration;
using System.Runtime.InteropServices;

namespace SuperCool.ServiceLibrary.Client
{
    [Guid("4DE3E259-EC52-4841-9D5B-B9A3AA095B51")]
    [SupportedServiceApplication("12A1BE54-04E7-4457-B8FE-6E11647E7EA6", 
        "1.0.0.0", typeof(SuperCoolServiceApplicationProxy))]   
    public class SuperCoolServiceProxy : SPIisWebServiceProxy, IServiceProxyAdministration
    {
        public SuperCoolServiceProxy() { }
        public SuperCoolServiceProxy(SPFarm farm) : base(farm) { }

        /// <summary>
        /// Gets an array of the service application proxy types supported by the service proxy.
        /// </summary>
        Type[] IServiceProxyAdministration.GetProxyTypes()
        {
            return new Type[] { typeof(SuperCoolServiceApplicationProxy) };
        }

        /// <summary>
        /// Gets a description of the specified service application proxy type.
        /// </summary>
        /// <param name="serviceApplicationProxyType">The service application proxy type.</param>
        /// <returns>A description of the specified type suitable for display.</returns>
        SPPersistedTypeDescription IServiceProxyAdministration.GetProxyTypeDescription(
            Type serviceApplicationProxyType)
        {
            if (serviceApplicationProxyType != typeof(SuperCoolServiceApplicationProxy))
                throw new NotSupportedException();

            return new SPPersistedTypeDescription(
                "Super-Duper Super Cool Service Proxy",
                "Connects to a Super Cool Service Application for managing super cool things.");
        }

        /// <summary>
        /// Creates an instance of the specified service application proxy type.
        /// </summary>
        /// <param name="serviceApplicationProxyType">The type of proxy to create.</param>
        /// <param name="name">The name of the new proxy.</param>
        /// <param name="serviceApplicationUri">The address of the connected service application.</param>
        /// <param name="provisioningContext">Context for the provisioning operation.</param>
        /// <returns></returns>
        SPServiceApplicationProxy IServiceProxyAdministration.CreateProxy(
            Type serviceApplicationProxyType, string name, Uri serviceApplicationUri, 
SPServiceProvisioningContext provisioningContext) { if (serviceApplicationProxyType != typeof(SuperCoolServiceApplicationProxy)) throw new NotSupportedException(); return new SuperCoolServiceApplicationProxy(name, this, serviceApplicationUri); } } }

 

Next, the ACTUAL code that is the proxy is responsible for setting up the load balancer and reading the client configuration. You’re going to want to scale your service application across multiple app servers—we need redundancy for our Hello World service app!!! But again, the code is relatively simple. You should be able to refactor the following code without effort. Following is a simple implementation of the SPIisWebServiceApplicationProxy. Next to the SPIisWebServiceApplication class, this is the next most important piece of the puzzle. There isn’t TOO much in here though, we’ll actually code much of the proxy logic into the client.

using System;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using System.Configuration;
using Microsoft.SharePoint.Utilities;
using SuperCool.ServiceLibrary.Services;

namespace SuperCool.ServiceLibrary.Client
{
    [System.Runtime.InteropServices.Guid("D49DF84A-77E3-47f9-85D3-965FA3D9E305")]
    [IisWebServiceApplicationProxyBackupBehavior]
    public class SuperCoolServiceApplicationProxy : SPIisWebServiceApplicationProxy
    {
        [Persisted]
        private SPServiceLoadBalancer loadBalancer;
        public SuperCoolServiceApplicationProxy(){}

        /// <summary>
        /// Constructs a new service application proxy.
        /// </summary>
        /// <param name="name">The name of the new service application proxy.</param>
        /// <param name="serviceProxy">The parent service proxy.</param>
        /// <param name="serviceApplicationAddress">
        /// The URI of the service application to which the new proxy will connect.</param>
        public SuperCoolServiceApplicationProxy(string name, SuperCoolServiceProxy serviceProxy, 
            Uri serviceApplicationAddress)
            : base(name, serviceProxy, serviceApplicationAddress)
        {
            // Create a new round-robin load balancer
            this.loadBalancer = new SPRoundRobinServiceLoadBalancer(serviceApplicationAddress);
        }

        /// <summary>
        /// Gets the display name that describes the object in the admin UI
        /// </summary>
        public override string TypeName
        {
            get { return "Super Cool Application Proxy"; }
        }

        public override void Provision()
        {
            this.loadBalancer.Provision();
            base.Provision();
        }

        public override void Unprovision(bool deleteData)
        {
            // Unprovision the load balancer
            this.loadBalancer.Unprovision();
            base.Unprovision(deleteData);
        }

        /// <summary>
        /// Gets the load balancer used by the service application proxy.
        /// </summary>
        internal SPServiceLoadBalancer LoadBalancer
        {
            get
            {
                return this.loadBalancer;
            }
        }

        /// <summary>
        /// Gets the configuration used by the service application proxy.
        /// </summary>
        internal Configuration Configuration
        {
            get
            {
                return OpenClientConfiguration(SPUtility.GetGenericSetupPath(
                    @"WebClientsSuperCoolServiceApplication"));
            }
        }
    }
}

With those classes, SPIisWebServiceApplication (covered in the last post), SPIisWebService, SPIisWebServiceInstance, SPIisWebServiceProxy and SPIisWebServiceApplicationProxy, we’ve got the SharePoint plumbing part of our service application library completed. Nothing too complex, but a lot of redundant code. In the next post, we’ll create our Hello World WCF service and we’ll cover the WCF plumbing, and then we’ll look at deploying it in the WSP project. After that, we’ll circle back and talk about databases and timer jobs within the service application framework.

About Daniel Larson: I am a SharePoint Architect at NewsGator, (the Social Platform for SharePoint 2010) and author of the best-selling Microsoft Press books Inside Microsoft Windows SharePoint Services 3.0 (with Ted Pattison) and Developing Service-Oriented AJAX Applications.

Dec 292007
 

Because what could be funner on a cold winter’s day than playing with threads? (Here is where I lose my non-dev readers… :)

If you’re writing code today, there’s no reason not to take advantage of today’s hardware. Generally, we write code for high-end servers but we don’t always get the chance to optimize our code for them.  A lot of us are still stuck on 32bit workstations on older single core hardware. A lot of developers I know are still using Xeon hyperthreaded processors from 2005 (you know who you are)… ah, those were the days!

Without utilizing threading for multicore architecture, your code is potentially two times as slow as optimized code. Threading is tricky though– so make multithreaded and thread-safe programming one of your new years resolutions for 2008. You should also realize that ASP.NET applications run as multithreaded apps… I recently heard of a (really smart) friend who used non-thread safe code in an ASP.NET application and it took down the server due to threading deadlocks. Don’t ignore it or it will bite you! (And be sure to debug, test and load test your apps in multicore systems! So much for virtualization… sigh…)

So, back to threading fun. You’ll need a multicore box to play this game. (It really isn’t going to be fun on a single core box… so get that hardware refresh scheduled!) I’m also not sure if it’ll work on Intel boxes… so you’ll need an AMD box.

Get the AMD Code Analyst here:
AMD CodeAnalyst™ Performance Analyzer for Windows (You may need to register with AMD’s developer center first– but that’s free and painless.) This tool works with managed and unmanaged code…

Justin Whitney wrote an excellent article about using the tool here:
Optimizing for Multi-Core with AMD CodeAnalyst 

To play the game, run the Code Analyst on your apps on a multicore box. Refactor some of your methods to use threading– check out MSDN’s listing on ThreadStart delegate here: http://msdn2.microsoft.com/en-us/library/system.threading.threadstart.aspx.

Where will this be the most beneficial? Data intensive processing, asyncronous database/web service calls, and Windows services. Threads for the sake of threads is a BAD idea… but if you’ve got a good place to utilize them, using multiple threads to accomplish your tasks can give you a great performance improvement.