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.

25 comments:

Jeremy Thake said...

Thanks for your help on this stuff, I've uploaded my presentation webcast from the Australian Conference covering all this which your readers might find interesting.

Elgolfin said...

Powershell Feature Upgrade is possible:

$site = get-spsite("http://your-site")
$site.Features["feature-guid"].upgrade($false)

Chris O'Brien said...

@Elgolfin,

Well sure, we can do anything with any .Net API in PowerShell :)

However, without the abstraction that cmdlets can provide, this gets messy for scenarios which are perhaps more real-life than your example of upgrading a single site-scoped Feature instance in a single site collection. If we wanted to upgrade all Feature instances which can be upgraded, we'd then need extra script to run QueryFeatures() etc. etc. So I definitely think there's a need to upgrade Features using PS in a "one script command" way.

If we took your point to it's logical conclusion, does that mean there was no point in Microsoft writing the 500+ PS cmdlets they supplied? :)

Chris.

Ram's World said...

hi Chris,
Let me if this works, the same on Moss 2007, we have been working on Enterprise deployments for Sharepoint specifically with WCM based projects.

Our project is divided into phases, wherein in Phase I, we have some features going live and Phase II the balance features. I would need to understand if while doing fixes and enhancements for Phase I features, can we use the same framework.
I would need some guidance as how do i upgrade
master pages,
layout pages,
site columns,
content types,
workflow and workflow associations,
Infopath forms,
sharepoint lists, libraries,
application pages (deployed within layout folder),
Custom CSS,
javascript files,
control templates (for user controls),
custom field types,
web parts (OOTB and Custom),
site definitions,
Custom XSL in style libraries specific to CQWP,
Layout images,
preview images for master pages,
multiple additions of webparts on pages,
webparts added to existing pages.

all the above upgrades, changes and fresh deployments.

ram

Chris O'Brien said...

@Ram,

I'm not 100% clear what you're asking, but the first thing to say is that the Feature upgrade framework is part of SharePoint 2010, not 2007. However, it is still possible to upgrade artifacts like those you list on 2007, it's just not as convenient or obvious how to do it. Some artifacts can just be overwritten on the filesystem (e.g. master pages/layouts which were deployed as a Feature) - others require code to upgrade (e.g. content types).

I covered this in detail (for nearly all of the artifacts you list) in my ALM presentation at the SharePoint Evolutions conference, my deck is available here at http://www.sharepointnutsandbolts.com/2010/05/sharepoint-2010-alm-my-slide-decks-from.html

HTH,

Chris.

Luke said...

In the paragraph at the bottom of the "When would I use Feature upgrade?" section you mention FeatureActivating. Is this correct? I was under the impression the code would execute under FeatureUpgrading? You mention it twice so I just want to clarify if it actually runs under activating or upgrading.

Thanks,

Luke said...

Chris,

Sorry, in my previous comment I mis-spoke about the location of my confusion. The question I have is about the bullet point named CustomUpgradeAction element which is right above the code for FeatureUpgrading. You say "the code will always be the FeatureActivating method in the receiver." This is the part that is confusing me. Shouldn't this be the FeatureUpgrading method?

Chris O'Brien said...

@Luke,

Yes, this was a typo and you were absolutely right, I meant the FeatureUpgrading event. I actually noticed this the other day too but forgot to correct it.

Text now updated. MANY THANKS!

Chris.

A.Ragab said...

Fantastic .. such an elegant style of writing. Thanks Chris

Jonathan B. said...

Best post about upgrades so far. Thanks a lot! Have a question please: What if I just want to add a new site column to an existing content type that was defined in a feature I do not own? In this case, I cannot upgrade the feature, since I don't own it.

Chris O'Brien said...

@Jonathan,

Good question. I actually think there's nothing stopping you using the AddContentTypeField with a content type provisioned by a Feature you do not own. Remember that all you're doing is specifying the ID of the content type and the ID of the field to add.

If I'm wrong, the fallback would be to use the imperative option (i.e. write code in the FeatureUpgrading event) and add the field that way.

HTH,

Chris.

Anonymous said...

Hi Chris,



Thanks for this excessive series, it's all very clear to me.



While playing with some feature upgrades, I stumbled upon the following problem:

I've got a feature with version 1.0.0.0. I created an upgrade to 2.0.0,0 and updated the package via STSADM. Then I called the .Upgrade method. However, there was an error in the upgrade process so the upgrade failed.
I corrected the error in Visual Studio, updated the package on the server and tried to upgrade the feature again but then discovered that the feature already has version 2.0.0.0.

Is there a method to completely revert feature upgrade after an upgrade failure?

Thanks in advance for your reply!

Best regards,

Maarten

Chris O'Brien said...

@Maarten,

It's a good question, and it came up as a question on part 3 of this series too. For convenience, here's what I said to that commenter:

That's a great question - you do indeed need to test *multiple* Feature upgrade iterations as you develop your Feature upgrade XML/code. You cannot specifically "rollback" an upgrade as such, but by reverting back the version number to the real one you want to use (e.g. 1.0.0.1) and *reinstalling* the Feature (i.e. by retracting/redeploying the WSP rather than upgrading), the end result is that the Feature is installed at that version with the current Feature contents. You can then use this as the version of your Feature to take forward to other environments. In other words, SharePoint does not maintain historic versions of a Feature's state.

Hope that makes sense.

Chris.

Anonymous said...

Hi Chris,

Thanks for the answer and sorry for my bad English. Excessive is of course not the right word, it should have been extensive (or a better soutable word) of course :-)

Anonymous said...

Chris,

Working through the upgrade process. Is it possible to add a visual web part to the feature? I am getting an error of The solution file located at "" does not appear to be a valid upgrade for solution "". the two solutions must have the same resource types (global or Web-Application scoped). It is a Site feature that originally was globally deployed. I was able to run custom actions but as soon as I add the web part I can no longer upgrade. Thank You.

Chris O'Brien said...

@Anonymous,

In adding a web part (visual or not), you've changed the scope of the WSP from global to web application-scoped. You cannot upgrade WSPs to deploy this change, it must be retracted and redeployed.

Take care to ensure you know effect this will have. For example, will any Features be automatically activated causing undesirable side-effects? I often snapshot my dev environment during this process.

HTH,

Chris.

mrmorodo said...

Hi Chris,

I have been reading up on SharePoint feature updating best practice and have found your series very helpful.
I thought you might like a bit of feedback regarding MapFile. I haven't used it personally but found the following information in an MS intial publication:

'MapFile — Allows you to map an uncustomised file to a different location on the front-end Web server. You can use the FromPath and ToPath attributes to rename a file in a Feature (for example, , but you can also use MapFile to move a file. In this case, the FromPath and ToPath attributes specify paths that are relative to the TEMPLATE directory. For example, if a Feature named "MyFeature" has .gif files installed in a "Gifs" directory (such as, %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\TEMPLATE\FEATURES\MyFeature\Gifs\basketball.gif), and in version 2 you want to rename the directory from “Gifs” to “Images”, then can be used to move the files.'

see:
http://msdn.microsoft.com/en-us/library/ie/ee537575.aspx

Regards,
Gareth.

Chris O'Brien said...

@mrmorodo,

Yes I read that too. In any case, appreciate the comment.

Cheers,

Chris.

jkps said...

In the first feature code should there be a closing last but one line?

Chris O'Brien said...

@jkps,

Yes you're right, it's missing a closing </UpgradeActions> element. Sorry about that, well spotted :)

Chris.

Sam said...

Excellent post Chris.

I have ran into an issue I am not sure how to resolve. My project was upgrading just fine at first after following your post. Then recently I am running into where it wont fire the FeatureUpgrading event anymore no matter what I do. My farm is patched to SP1, do you know of any bugs that get fixed with the FeatureUpgrading in CU's after SP1?

Thank you

Chris O'Brien said...

@Sam,

Hmm, I'm not aware of any changes. When you use one of the QueryFeatures() methods to test for Feature instances requiring upgrade, does your Feature get listed? If not, are you doing anything differently with incrementing the version number and deploying the WSPs?

Thanks,

Chris.

Sam said...

Thank you for getting back to me.

Yes, after I do the Update-SPSolution via PowerShell I can see that the feature shows its ready for an upgrade (both by doing QueryFeatures() and using your "Manage Feature Upgrades" in your SPFeatureUpgrade kit).

In my FeatureUpgrading event I am just removing the ReadOnly off my content type (something simple to test).

My initial version is 1.0.0.0 and so I increment it to version 2.0.0.0 with is within the VersionRange in my UpgradeActions.

I upgrade using your "Manage Feature Upgrades". I can see the system does the upgrade because I run a PowerShell script to show me the version before the upgrade and the version after the upgrade and I can see it increments from 1.0.0.0 to 2.0.0.0. But when I add a debug breakpoint to the FeatureUpgrading event it never gets hit. And when I turn up the diagnostic logging I don't see the event get triggered in the SharePoint logs (I do see other events get triggered such as FeatureInstalling and FeatureActivated).

As I had stated, it was working at first. Then I added more to my CustomUpgrades to add more columns. After that I an unable to get the event to trigger again. So this led me to think that maybe I am hitting an issue or bug in SharePoint. Very odd.

Chris O'Brien said...

@Sam,

Maybe one thing to try would be to reinstall/activate the Feauture in dev to start from a clean state. It would be interesting to see if you hit the same issue again.

Otherwise I can't say I've seen this before I'm afraid.

All the best,

Chris.

Jörg Spilker said...

How do i handle cases where only code changes in a feature. For example, i have a class with some helper methods in a feature which is used by many other features. There are now two scenarios i can imagine. I added a new helper method. In this case i would only change the AssemblyFileVersion, not the AssemblyVersion. Do i just have to Update-SPSolution with the new feature file to replace the file in the GAC? I think i don´t habe to call feature.upgrade for any instance in this case.

Second scenario is that some existing functionality is changed but features depending on this feature should still work. In this case i need to have two versions of the assembly in the GAC. I think this is a case where the Mapfile directive will help me?