SharePoint 2010 Code Deployment, Part Two

Home » SharePoint Development » SharePoint 2010 Code Deployment, Part Two

SharePoint 2010 Code Deployment, Part Two

Posted on

Part One of this series gave a general overview of SharePoint 2010 code deployment using Visual Studio 2010. Now it’s time to dig deeper and take an in-depth look at how developers can customize the deployment process. We will begin at the lowest level by creating some custom SharePoint Commands, then move into creating Deployment Steps which utilize those commands and a Deployment Configuration that combines the Steps into a single deployment option. Finally, we will package it all together and use the Visual Studio Extensions and Managed Extensibility Framework to deploy our customizations as a VSIX package.

Introduction

The basic unit of work for 2010 code deployment is the SharePoint Command. This is how developers interact with the SharePoint Object Model during the deployment process. Due to the fact that SharePoint Commands must run in a separate process to bridge the 32-bit/64-bit divide, they must be created inside of a separate project. How the project is designed, the number of classes in each project, and which methods to include in each class, are decisions left entirely up to the developer. As I mentioned previously, there are as of yet no best practices relating to the composition of custom deployment projects, but for ease of maintenance and reusability I would recommend that each class function as a relatively self-contained unit with the minimum number of methods required to achieve the desired functionality. As time progresses, it may be useful to have a base utility class which implements helper methods used by various other classes, but for the time being we will assume that all the necessary code will reside within a single class.

The scenario for this example is pretty straightforward. We will create a List Definition and an associated List Instance. Upon deployment, the SharePoint Command will check to see if there are any existing List Instances associated with the List Definition Feature and remove them if it finds any before deploying a new version of the List Definition. This will insure that there are no orphaned lists which inherit from the previous Template.

Setting Up the Project

In order to implement a custom Deployment Configuration for our scenario, we will need two separate Visual Studio solutions – one for the List Definition/List Instance and one for the custom deployment. I won’t go into detail on the first solution as it can easily be implemented using the new SharePoint Project Items and following the prompts for creation of a List Definition. The following graphic illustrates the solution:

Eric Shupps Eric Alan Shupps eshupps @eshupps SharePoint Cowboy BinaryWave SmartTrack

Once this is in place we can move on to creation of the deployment solution. This solution will require three separate projects – one for the VSIX package, one for the Deployment Step and Deployment Configuration, and one for the SharePoint Commands. They are separated logically by type of functionality as well as.NET Framework requirements – the VSIX package and Deployment Step/Configuration projects require .NET 4.0 while the SharePoint Command project requires .NET 3.5. We will begin with the SharePoint Command project as it is at the bottom of the stack from a referential perspective (the Deployment Step references the SharePoint Commands, the Deployment Configuration references the Deployment Step, and the VSIX project references them all).

Begin by creating an empty solution and adding a class project for the SharePoint Commands. Be sure to set the .NET Framework version to 3.5 for the SharePoint Commands project, which in this example is using the Contoso.DeploymentCommands namespace:

Eric Shupps Eric Alan Shupps eshupps @eshupps SharePoint Cowboy BinaryWave SmartTrack

Writing the Code

There are two distinct pieces of functionality in this command. First, it will be necessary to determine if any lists deriving from the List Definition Feature exist. This will enable the subsequent Deployment Step code to determine if it should continue executing or terminate and log a message to the IDE. Second, if any lists do exist, they will have to be removed from the list collection of the target site. There is no need to pass any state information back to the Deployment Step in this scenario; however, other command types may require success or fail messages to be transmitted, which can be achieved by setting the method type to ‘string’ and returning an appropriate value, or by using an integer count, or by other various means.

It will be necessary to reference the Micrsoft.VisualStudio.SharePoint.Commands namespace (which is included with the Visual Studio Beta 2 SDK) along with the standard Microsoft.SharePoint namespace. This is the only project within the custom deployment solution in which we can directly access the SharePoint object model due to the fact that Visual Studio is a 32-bit application while SharePoint 2010 is limited to only 64-bit execution.

Attributes and Naming Conventions

All SharePoint Commands are referenced by their literal string name which is defined as part of an attribute decoration on each method. There are no explicit rules pertaining to naming conventions; however, as the use of custom commands grows within a development team it may be necessary to enforce some kind of explicit notation. For this example we will use a dotted hierarchy which includes the namespace but this is simply an arbitrary choice and has no bearing on the overall functionality.

Methods

Begin by creating the SPCommand class (again, no explicit naming conventions are required and it may be necessary to come up with a more structured naming convention over time).

using System;
using Microsoft.SharePoint;
using Microsoft.VisualStudio.SharePoint.Commands;

namespace Contoso.DeploymentCommands
{
  
public class SPCommand
  
{
  
}
}

Next, we will add the first method which determines if a List Instance deriving from the specified List Definition Feature exists. We will decorate the method with an attribute which specifies a name for future reference by the Deployment Step.

using System;
using Microsoft.SharePoint;
using Microsoft.VisualStudio.SharePoint.Commands;

namespace Contoso.DeploymentCommands
{
  
public class SPCommand
  
{
     
[SharePointCommand("DeploymentCommands.SPCommand.ListExists")]
     
public bool ListExists(ISharePointCommandContext context, string featureId)
     
{
        
Guid templateId = new Guid(featureId);
        
bool bList = false;
         using(SPWeb web = context.Web)
        
{
           
SPListCollection lists = web.Lists;
           
foreach (SPList list in lists) 
            
{
              
if (list.TemplateFeatureId.ToString() == templateId.ToString()) 
              
{
                 
bList = true; 
                  
break;
              

            
}
        
}
        
return bList; 
      
}
  
}
}

Note that the method is invoking the new ISharePointCommandContext interface. This interface provides contextual information for the command which is passed in from the Deployment Step. We are also capturing the ID of the List Definition Feature as the code will need this ID to identify any derived List Instances. The method itself simply iterates the SPListCollection object and identifies any SPList objects whose TemplateFeatureId property matches the GUID passed in from the Deployment Step. We only need one match to satisfy our requirements, so a Boolean value is set when a match is found and this value is returned to the Deployment Step. If no match is found it returns the default value of ‘false’. Also take note of the fact that the SPWeb object is already available to us via the context, much like an Event Receiver or Feature Receiver.

Now that we have code to determine if a list associated with the specified Feature exists, we can add a method to perform the task of removing such lists from the list collection. As with the previous method, begin by decorating the method with an attribute which specifies the name that will be used in the Deployment Step, then add code to iterate the list collection, match the TemplateFeatureId value, then remove the list object and update the SPWeb object.

[SharePointCommand("DeploymentCommands.SPCommand.DeleteLists")]
public void DeleteLists(ISharePointCommandContext context, string featureId)
{
  
Guid templateId = new Guid(featureId);
  
using (SPWeb web = context.Web)
  
{
     
SPListCollection lists = web.Lists;
     
for (int i = lists.Count – 1; i >= 0; i–)
     
{
        
if (lists[i].TemplateFeatureId.ToString() == templateId.ToString())
        
{
           
lists[i].Delete();
           
web.Update();
        
}
     
}
  
}
}

We now have two commands, DeploymentCommands.SPCommand.ListExists and DeploymentCommands.SPCommand.DeleteLists, which encapsulate the functionality required by the Deployment Step. As previously mentioned, you may wish to add error handling logic in the DeleteLists method and return a string, integer or Boolean value which can then be used by the Deployment Step to determine whether or not the lists were actually deleted, or even log a message with the list names or ID’s that were removed.

Building and Packaging

At this stage there is nothing more to be done that to build the project and insure it compiles correctly. Later, in the VSIX portion, we will discuss how to include the resultant assemblies in the final deployment package.

Miscellaneous Considerations

It is worth pointing out that the deployment process and execution of SharePoint Commands is not an area in which much can be done to enhance performance. Certainly, if the commands being executed are enumerating list items or accessing external data sources, then best practices should be employed in order to avoid lengthy wait times, but beyond that there are very few options for reducing the overhead incurred by the necessity of invoking and terminating a separate process to execute the command code, the processing done by SharePoint itself, recycling of application pools, and any extensions or add-ons to the Visual Studio IDE. In other words, don’t expect the deployment process to be exceptionally fast but do take measures to insure that the command code being executed doesn’t exacerbate the problem.

Testing

It should be fairly obvious that the SharePoint Command code is just as testable as any other code running against the SharePoint Object Model. From a Unit Test perspective, developers can approach SharePoint Commands as stand-alone classes which are developed and tested outside of the deployment process using whatever methods they prefer – TypeMock, NUnit, XUnit, PEX/MOLES, etc. By structuring the projects in the manner suggested, the command methods may be fully tested prior to adding the dependencies upon the deployment interfaces (SharePointCommand, ISharePointCommandContext), which may then be added after the test cases are satisfied. Obviously, this means that subsequent testing will have to account for the integration issues brought about by including the deployment interfaces. One possible solution to this problem would be to abstract each of the core methods into a separate set of utility classes which receive the context objects and parameters from the command. This would permit the use of mock objects or parameterized tests without requiring the deployment framework dependencies.

To illustrate this, consider separating the code into two separate classes and abstracting the core functionality:

SharePoint Commands

using System;
using Microsoft.SharePoint;
using Microsoft.VisualStudio.SharePoint.Commands;

namespace Contoso.DeploymentCommands
{
  
public class SPCommand
  
{
     
[SharePointCommand("DeploymentCommands.SPCommand.ListExists")]
     
public bool ListExists(ISharePointCommandContext context, string featureId)
     
{
        
bool bList = Contoso.DeploymentCommands.Utilities.VerifyListByTemplateId(context.web, featureId);
        
return bList;
     
}
     
[SharePointCommand("DeploymentCommands.SPCommand.DeleteLists")]
      public void DeleteLists(ISharePointCommandContext context, string featureId)
     
{
        
int i = Contoso.DeploymentCommands.Utilities.RemoveListByTemplateId(context.web, featureId);
        
if (i < 1)
           
throw new System.Exception();
     
}
  
}
}


Utility Class

using System;
using Microsoft.SharePoint;

namespace Contoso.DeploymentCommands
{
  
public class Utilities
  
{
     
public bool VerifyListByTemplateId(SPWeb web, string TemplateId)
     
{
        
Guid templateId = new Guid(TemplateId);
        
bool bList = false;
        
using(web)
        
{
           
SPListCollection lists = web.Lists;
           
foreach (SPList list in lists)
           
{
              
if (list.TemplateFeatureId.ToString() == templateId.ToString())
              
{
                 
bList = true;
                 
break; 
               
}
           
}
        
}
        
return bList;
     
}
     
public int RemoveListByTemplateId(SPWeb web, string TemplateId)
     
{
        
Guid templateId = new Guid(featureId);
        
int x = 0;
        
using (web)
        
{
           
SPListCollection lists = web.Lists;
           
for (int i = lists.Count – 1; i >= 0; i–)
           
{
              
if (lists[i].TemplateFeatureId.ToString() == templateId.ToString())
              
{
                 
lists[i].Delete();
                 
web.Update();
                 
x = x + 1;
              
}
           
}
        
}
        
return x;
     

   
}
}


Conclusion

SharePoint Commands provide a means for developers to interact with the SharePoint Object Model when creating custom deployment solutions. They run in a separate process in order to overcome the x86/x64 limitations of Visual Studio with SharePoint and are flexible enough to encapsulate any OM-related functionality. They also serve as the base building block for deployment customizations. In Part Three of this series we will explore the next link in the chain, Deployment Steps, how they relate to and reference SharePoint Commands, and how they are used within the Visual Studio IDE.

SharePoint 2010 Code Deployment, Part One

SharePoint 2010 Code Deployment, Part Three