Wednesday, 23 June 2010

Feature upgrade (part 1) - fundamentals

In this article series:

  1. Feature upgrade (part 1) – fundamentals (this article)
  2. Feature upgrade (part 2) – a sample to play with
  3. Feature upgrade (part 3) – introducing SPFeatureUpgrade kit
  4. Feature upgrade (part 4) – advanced scenarios
  5. Feature upgrade (part 5) – using PowerShell to upgrade Features

In recent articles I’ve touched on the introduction of versioning and upgradability to the Features framework in SharePoint 2010. I’ve covered this topic recently in conference talks (SharePoint Evolutions) and as a chapter in the forthcoming Real World SharePoint 2010 book – however I also want to cover it to a certain level here, as I’m mindful that not everyone came to the talks or will buy the book. I also want to introduce a tool I’ve published on Codeplex which might be useful if you decide to use Feature upgrade - more on this in the next article.

When would I use Feature upgrade?

Feature upgrade is useful in the following scenarios (though there may be more!):

  • To make changes to existing site collections or sites, or something inside – for example:
    • Making changes to existing items e.g. adding a new column to a content type/list
    • Adding new elements to existing sites e.g. a new list
    • Any change which involves doing something to a site using the API e.g. using code to modify the navigation settings for many sites
  • To add new functionality into an existing Feature, rather than create a new one – perhaps because that’s the most logical factoring
  • Where some functionality will be upgraded several times during it’s lifecycle, possibly in a situation where the changes are not rolled out to every site, or are rolled out at different times

If you’re modifying or expanding on functionality developed using Features (across many sites or just one), then Feature upgrade is likely to be a good vehicle to roll out your changes. This is thanks (in part) to the new QueryFeatures() methods in the API, which provide a convenient collection to iterate over to apply the changes. When the changes themselves are also implemented in code, in the past developers may have put their implementation in the FeatureActivating event and then ensured the Feature is deactivated/reactivated - however this came with baggage, since you might have had other code in there which was never intended to be re-run. Feature upgrade is designed to solve such problems.

What does Feature upgrade look like?

Microsoft added some new XML to the Features framework to support Feature upgrade. When a new version of a Feature is created, this may involve:

  • Incrementing the version number of an existing Feature (this is mandatory for Feature upgrade to happen)
  • Adding some new XML to define new items for the Feature
  • Writing some code to execute in the new FeatureUpgrading event in the Feature receiver
  • All of the above

Here’s an example of an upgraded Feature highlighting some of the possibilities:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/" Version="1.0.0.0">
  <UpgradeActions>
    <VersionRange BeginVersion="0.0.0.0" EndVersion="0.9.9.9">
      <ApplyElementManifests>
        <ElementManifest Location="SomeFunctionality_Iteration2\Elements.xml" />
      </ApplyElementManifests>
      
      <AddContentTypeField ContentTypeId="0x010073f25e2ac37846bb8e884770fb7307c7"
          FieldId="{536DC46C-DC26-4DB0-A97C-7C21E4362A85}" PushDown="TRUE"/>
      <AddContentTypeField ContentTypeId="0x010073f25e2ac37846bb8e884770fb7307c7"
          FieldId="{4E7A6719-011A-47EA-B983-A4941D688CA6}" PushDown="TRUE"/>
 
      <CustomUpgradeAction Name="UpdateSomething">
        <Parameters>
          <Parameter Name="PassSomeValue">This is a string</Parameter>
        </Parameters>
      </CustomUpgradeAction>
    </VersionRange>
</Feature>

[Sidenote] Note that the above XML is actually a subset of the full feature.xml file – when working with Feature upgrade, it is necessary to step outside of the Feature Designer in Visual Studio 2010 and edit the XML files directly (the old-fashioned way!). When doing this the best choice is to allow VS to merge your changes with the XML it is managing. The XML being managed by VS gets merged with your custom XML when the WSP is packaged – if you could this bit of the XML isolated (you can’t, since you never need to), this might look like this:

<Feature Title="Some functionality" Id="cae1f65d-0365-42e9-9907-356c7983e902" Scope="Site">
  <ElementManifests>
    <ElementManifest Location="SomeFunctionality\Elements.xml" />
    <ElementManifest Location="SomeMoreFunctionality\Elements.xml" />
  </ElementManifests>
</Feature>

Essentially Visual Studio will still manage our element manifests, but any XML around Feature upgrade needs to be edited by hand. Walking through the box containing the main XML, we can see:

  • The Feature has a Version attribute (to be incremented each time the Feature is upgraded)
  • A VersionRange element defining the upgrade steps to process for a particular upgrade i.e. when an existing Feature instance within the BeginVersion and EndVersion is upgraded with an updated Feature definition
  • An ApplyElementManifests element – this is used to add new elements to an existing Feature. When the Feature is upgraded, any items (e.g. content types, modules etc.) will be provisioned according to the element manifest(s)
  • AddContentTypeField element – this is a convenience mechanism for the common task of adding a field to an existing content type (a very common upgrade scenario). Note the PushDown attribute – this is hugely useful as it does the work of pushing down the change from the site content type to all list content types within the site (and therefore, all lists), all without any code.
  • CustomUpgradeAction element – this allows the developer to point to some code to run to perform the upgrade actions. It will be common to need this approach, given the vast array of things you might want to do in an upgrade. In terms of the ‘pointing’, in fact the code will always be the FeatureUpgrading method in the receiver, but the value passed in the Name attribute is passed to this method to identify which code to run (along with any parameters). Hence your FeatureUpgrading method is likely to contain a switch statement and would look something like this if it was to match up with the above XML:
    public override void FeatureUpgrading(SPFeatureReceiverProperties properties, string upgradeActionName, System.Collections.Generic.IDictionary<string, string> parameters)
    {
        SPWeb parentWeb = (SPWeb)properties.Feature.Parent;
     
        switch (upgradeActionName)
        {
            case "UpdateSomething":
                string someValue = parameters["PassSomeValue"];
                // do some stuff.. 
                break;
            default:
                break;
        }
    }

In addition to these possibilities there is one further declarative element – MapFile. MapFile allows you to repoint the location of an uncustomized file, this will literally update the pointer in the database. The precise scenarios where you’d want to use this (as opposed to simply deploying an updated version of the original file) escape my tiny mind unfortunately – the only thing I can think of is that if it allows the repointing to be done at different scopes (e.g. web), in a DelegateControl kind of way, that could be very useful. I’m unable to verify this however as I just cannot get MapFile to work, and neither can others (@jthake) that I know have tried. Oh well.

Taking a step back to look at these tools, it’s easy to think that if you don’t happen to be adding a field to a content type then realistically you’re looking at code. However ApplyElementManifests is often all you need for some scenarios e.g. a set of new fields + a new content type + a new publishing page layout.

Notes

These are some ‘fundamental’ things to know – I’ll discuss some more advanced aspects in a future article:-

  • Feature upgrade does NOT happen automatically (including when the Feature is deactivated/reactivated)! The only way to upgrade a Feature is to call SPFeature.Upgrade(), typically in conjunction with one of the QueryFeatures() methods. My tool which I’ll go on to talk about is a custom application page which helps you with this part – note there is no STSADM command, PowerShell cmdlet or user interface to do this out-of-the-box.
  • On the VersionRange element, BeginVersion is inclusive but EndVersion is not. In other words, a Feature instance will be upgraded if the current version number is equal to or greater than BeginVersion , and less than  EndVersion.
  • Upgrade instructions are executed in the order they are defined in the file.
  • If a Feature does not have a Version attribute, the version is 0.0.0.0.
  • Enabling logging can help diagnose any issues. In the ULS settings, under the ‘SharePoint Foundation’ category, set the following sub-categories to Verbose to see more info:
    • Feature Infrastructure
    • Fields
    • General

Summary

SharePoint 2010 introduces additional lifecycle management capabilities with the ability to version and upgrade Features. There are some declarative elements such as ApplyElementManifests and AddContentTypeField, but using the CustomUpgradeAction element allows you to shell out to code where necessary. The only way to actually perform upgrade on a Feature once it has been updated is to call SPFeature.Upgrade() on each instance of the Feature (e.g. in each web) which should be upgraded – new QueryFeatures() methods help you locate Feature instances which can be upgraded. I’ve written a custom application page which helps manage this process, to be discussed next time.

Thursday, 3 June 2010

Upgrading sandboxed solutions

I’ve had a big focus on Application Lifecycle Management and SharePoint 2010 recently, covering things such as the changes in the Solutions and Features frameworks, in particular to allow for ‘upgrading’ Features. I’ve not posted much info here (yet), but delivered the material over two sessions at the recent SharePoint Evolutions conference (you can get my slide decks here) and have a chapter in ‘Real World SharePoint 2010’ (aka the MVP book) on the topic. Today I want to briefly cover some info which I didn’t manage to talk about at the conference – specifically ALM and the sandbox. Just like farm solutions, most sandboxed solutions will most likely evolve over the course of their lifetime, with new functionality added and existing functionality refined - changes will be made to both code and artifacts (e.g. content types, list definitions etc.). Vendors will release new versions of their products, and clients will demand enhancements to other solutions they use which are delivered as sandboxed solutions. Importantly, the process for implementing such changes to existing apps is different between farm and sandboxed solutions – this post explores these differences.

Sidenote: the best starting point for this info is having an understanding of SP2010 ALM concepts such as Feature upgrade, better support for assembly versioning etc. - my slide decks linked earlier cover these, but I’ll try to provide some background with each point.

Difference 1 – upgrading Solution packages

Most devs and admins are familiar with upgrading WSPs using STSADM –o upgradesolution or the ‘retract/dedeploy’ cycle. In SharePoint 2010 much stays the same here aside from the addition of PowerShell commands such as Update-SPSolution. The key difference when upgrading sandboxed solutions is that a new filename for the WSP must be provided each time it is upgraded – the solution ID is then used internally to tie things together. I’m assuming this is because all versions of a sandboxed solution are stored in the Solution Gallery (unlike farm solutions, which only store the latest version once deployment/upgrade is complete) and of course, the Solution Gallery is fundamentally a SharePoint list – which, like any other, doesn’t allow multiple items with the same title. 

No doubt Microsoft could have chosen other options such as using list item versioning or making use of separate fields for name and title, but clearly these approaches were not preferred, so renaming WSPs it is. The logical approach will probably be to add version numbers into WSP filenames for sandboxed solutions.

Difference 2 – Feature upgrade and QueryFeatures()

The Feature upgrade framework is often a great way to make changes to existing SP2010 sites – in a nutshell, it is typically used in conjunction with the new QueryFeatures() method in the API, which helps you find Feature instances at different scopes which need upgrading. A Feature instance needs upgrading when the corresponding Feature.xml file in the SharePoint root has an incremented version number (e.g. 2.0.0.0) but SPFeature.Upgrade() has not yet been called for that instance. What happens is that you run QueryFeatures() on the appropriate object (e.g. SPSite to find Site or Web-scoped Feature instances), and then call SPFeature.Upgrade() for all items returned, assuming you wanted to upgrade the Feature everywhere. As an example, if you had a site collection with 50 webs and called SPSite.QueryFeatures(SPFeatureScope.Site, true), you would then get a collection of 50 Feature instances at Web scope and call SPFeature.Upgrade() on each one. Alternatively, you might choose to only upgrade selected webs – e.g. when only certain web should receive the updated functionality.

The key difference in the sandbox is that it is not possible to upgrade Feature instances selectively – upgrading the WSP has the effect of calling SPFeature.Upgrade() automatically for all instances. This means there is perhaps less flexibility in this area which could be important in some scenarios.

Difference 3 – issues with assembly versioning in the sandbox

A key improvement in ALM in SharePoint 2010 is increased support for versioning assemblies. WSP packages can now add BindingRedirect elements to web.config, meaning a reference to a 1.0.0.0 assembly can be redirected to (e.g.) 2.0.0.0. In some cases this works equally well in sandboxed and farm solutions, but my testing shows issues with sandboxed assemblies which contain web parts or other controls. Aside from the main issue, there is currently a minor gotcha with both sandboxed and farm solutions and SafeControl entries - the WSP deployment removes the 1.0.0.0 entry, and a 2.0.0.0 SafeControl entry alone isn’t sufficient so a “type not registered as safe” error occurs. However, note this can be mitigated by manually editing the solution manifest to add a 1.0.0.0 entry which will be merged with the automatically-generated entry for 2.0.0.0. Something seemingly more serious is that when versioning assemblies with web parts in the sandbox, even performing this mitigation step to ensure all BindingRedirect and SafeControl entries are correct results in a broken web part (error “Unhandled exception was thrown by the sandboxed code wrapper's Execute method in the partial trust app domain: An unexpected error has occurred”).

Since many web part vendors are expected to use the sandbox (in addition to the rest of us), this would mean that many are unable to leverage the versioning and release management improvements in the sandbox (for web parts at least). Other strategies such as using the FileAssemblyVersion instead for versioning may be necessary (doesn’t affect binding/SafeControls). Some folks in the Product Group are looping me in with the team who implemented versioning in the sandbox, so I’ll hopefully find out more and update this post. Right now I’m hoping there was a flaw in my (many) tests - if not, I’m hoping it’s something we might see fixed fairly soon. I’ll keep you posted.