One topic I’ve been somewhat surprised to not hear more then a trickle about from the community about SharePoint 2010 is the ability to upgrade Features. Features were of course introduced in SharePoint 2007 as a mechanism for provisioning elements within SharePoint. Those elements can be anything from Site Columns, Content Types, and Lists to Webparts and workflows just to name a few. It’s quite a powerful mechanism for provisioning reusable elements within SharePoint but one if its weaknesses has been the inability to formally support an upgrade process. Now that’s not to say that you couldn’t work around the issue but it was never elegant.
Old Technique #1 – Upgrade Feature In Place then Deactivate\Reactivate (Unsupported)
Although unsupported some elements of a feature didn't mind getting deactivated and reactivated to get an “update”. Although this technique is unsupported it could be used in scenarios where the provisioning engine could handle conflict resolution such as provisioning a file using the IgnoreIfAlreadyExists attribute on the File element of the Module element. However most elements such as Lists and Content Types will often throw exceptions during activation if there was a conflict so you had to be very careful in its usage. Not ideal at all.
The following is a table of a few of the common elements and how they deal with getting unprovisioned (deactivated). Keep in mind that there are many more elements within a feature that may be declared such as list definitions, controls, custom actions, etc., but these are just declarations not instances.
| Element |
Behavior |
| Content Type |
Unprovisioned ** |
| Field |
Unprovisioned ** |
| List Instance |
NOT Unprovisioned |
| Module (File) |
NOT Unprovisioned |
| ContentTypeBindng |
NOT Unprovisioned |
** These items will get unprovisioned but artifact may be left behind if instance of these definitions were already used.
As you can see this can get messy quickly. So please don’t go hitting your head against a brick wall when trying to understand why artifacts get left behind after you reactivate\deactivate your feature and you don’t get the expected results. I like to use an analogy that many developers are more familiar with – SQL Scripts. If you run a SQL script that creates a table and then update the SQL Script with a new column does the table get update – No. You have to upgrade the script to run an alter table SQL command. SharePoint is the same, once you create an instance of something you have to do something “different” to update it. Additionally SharePoint is very careful when it comes to removing elements upon deactivation of the parent feature, nobody likes data disappearing on them unexpectedly. Now it would be nice if it could perform some more advanced differencing\appending logic but unfortunately that isn’t the case (and still isn’t much with SharePoint 2010). Needless to say though this has been a sore point for many developers getting into Feature development for the first time.
Old Technique #2 – New Feature With Version Numbers (Supported)
Another common technique is to build multiple Features to mimic an upgrade process. This typically lead to Features with names such as “My Feature 1.0” and “My Feature 1.1” and so on and so forth. Although this worked this could get very messy very quickly and almost always led to code heavy Feature Receivers to get around the lack of conflict resolution in the provisioning engine. You could use Feature Dependencies to try to enforce a particular order but in the end you ended up creating your own upgrade engine\schema. Once again far from ideal.
Welcome to SharePoint 2010
So what's the big deal with with Features in SharePoint 2010? Well first off we can now make use of that previously useless Version attribute on the Feature element. Secondly, through the use of the Version attribute, SharePoint now provides the core framework for upgrading features. To support this framework SharePoint now keeps track of the version of a Feature each time it’s activated, for each scope, for each site. By keeping track of the version it can apply rules that are defined for different versions of the feature. You can also take advantage of a new query method to gain more control over who gets upgraded.
<UpgradeActions> Defining How To Upgrade – The Anatomy of the UpgradeActions Element
<Feature Id="817224F9-2209-4965-B18D-B73CA86AEFCA"
Title="My Feature"
Description="My Feature Details"
Version="2.0.0.0"
Scope="Site"
Hidden="FALSE"
DefaultResourceFile="core"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="Elements.xml" />
<ElementManifest Location="Elements2.xml" />
</ElementManifests>
<UpgradeActions>
<VersionRange EndVersion="2.0.0.0">
<ElementManifests>
<ElementManifest Location="Elements2.xml" />
</ElementManifests>
</VersionRange>
</UpgradeActions>
</Feature>
The <UpgradeActions> action is the core to the new Feature upgrade framework and adds several new elements in the Feature.xml that define the behavior of the upgrade process including VersionRange, ApplyElementManifests, AddContentTypeField, MapFile, CustomUpgradeAction.
VersionRange
The version range element defines the version range at which the UpgradeAction is applied. The BeginVersion and EndVersion attribute define the affected version. Multiple VersionRange elements are permitted to apply different upgrade rules to different versions of the feature. If you do not provide a BeginVersion the version range applies all versions up to the version specified in the EndVersion.
<UpgradeActions>
<VersionRange BeginVersion="2.0.0.0" EndVersion="2.5.0.0">
<ElementManifests>
<ElementManifest Location="Fields.xml" />
</ElementManifests>
</VersionRange>
</UpgradeActions>
ApplyElementsManifests
From a declarative upgrade standpoint the ApplyElementsManifests will be your opportunity to provision additional elements. Any valid element is permitted just as with a standard Feature. Keep in mind that the manifest will also need to be defined in the standard ElementManifests section of the Feature.xml as well as in the VersionRange ApplyElementsManifests elements.
AddContentTypeField
Content Types are a very powerful feature within SharePoint 2007 that allows you to declare reusable collections of meta data (columns) that can be reused across the site collection in any list. One nice feature of Content Types is the ability to push updates, such as new columns, to any list making use of the Content Type. Unfortunately from a declarative provisioning standpoint once your Feature declares a Content Type and its Site Columns (fields\columns) in SharePoint 2007 there was no out of the box mechanism to declaratively add new Site Columms to the Content Type. You had to use a Feature Receiver to drop down to code to do this.
With SharePoint 2010 you now can add additional Site Columns to a Content Type through the UpgradeActions element:
<UpgradeActions>
<VersionRange BeginVersion="2.0.0.0" EndVersion="2.5.0.0">
<ElementManifests>
<ElementManifest Location="NewFields.xml" />
</ElementManifests>
<AddContentTypeField ContentTypeId="0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007"
FieldId="{2ADDDEF9-3832-49B4-A5C4-CEDE34C37FDF}"
PushDown="TRUE" />
</VersionRange>
</UpgradeActions>
In this example we’re applying a new elements manifest that creates a new field and then adding that field to a previously defined content type and instructing the update to be pushed down to each instance of the content type being used (pushing down the update).
MapFile
MapFile allows you to change the path or name of an existing file you provisioned to a new location. This can be useful, if for example, you decided to change the path of your images folder from pictures\img.gif to images\img.gif. Not very handy on the surface but could be usefull in specific scenarios. If you’re defining module elements in a Feature you’ve likely already identified that a feature is going to get some reuse. Modules (files) provisioned by a feature live in the file system (until possibly customization) which means you’ll get some performance benefits as well. If you then want to upgrade the “virtual” location of these files within SharePoint you now have a mechanism to apply this change across the board. A bit niche but none the less handy.
<MapFile FromPath="Pictures\Image.gif" ToPath="Images\Image.gif" />
NOTE: During the first private beta I had the impression this applied to customized elements as well. I thought this would be a great feature to get control of a customized files but unfortunately this isn’t the case. I had mentioned this in a couple of my initial presentations on the subject so I wanted to clarify\correct in case you were in one of my sessions.
CustomUpgradeAction
Similar to the use of Feature Receivers to drop to code during the activation process the CustomUpgradeAction allows you to drop to code during an upgrade process. For example if you wanted to update existing instances of a list with additional views or possibly additional columns (if you didn’t use ContentTypes). This uses the existing Feature Receiver framework but works off a new function SPFeatureReceiver.FeatureUpgrading.
<UpgradeActions
ReceiverAssembly="ShinyNewFeatureReceiver, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3ef91b1292056a22"
ReceiverClass="ShinyNewFeatureReceiver.MyReceiver">
<VersionRange EndVersion="2.0.0.0">
<CustomUpgradeAction Name="RemoveField">
<Parameters>
<Parameter Name="ContentType">MemberProfile</Parameter>
<Parameter Name="FieldName">SSN</Parameter>
</Parameters>
</CustomUpgradeAction>
</VersionRange>
</UpgradeActions>
CustomUpgradeActions allow you to perform actions that fall out of scope of the out of the box upgrade actions. In the example above we want to remove the SSN field from the MemberProfile content type. CustomUpgradeActions are defined in a SPFeatureReceiver as a new method called FeatureUpgrading. The following is a snippet on its usage building on the above example and simplified a bit.
public override void FeatureUpgrading(SPFeatureReceiverProperties properties, string upgradeActionName, IDictionary<string, string> parameters)
{
SPWeb web = thisFeature.Parent as SPWeb;
if(upgradeActionName == "RemoveField")
{
string contentTypeName = parameters["ContentType"];
string fieldName = parameters["FieldName"];
SPContentType contentType = web.RootWeb.ContentTypes[contentTypeName];
contentType.Fields[fieldName].Delete();
contentType.Update(true);
}
}
ActivationDependencies
ActivationDependencies are available in SharePoint 2007 and SharePoint 2010 but in order to support the versioning concept have been adapted in SharePoint 2010 to specify the Minimum Version required for the dependency. So you could place an activation dependency on the 2.0.0.0 version of another Feature in case you are dependant on a new element in the latest version.
Querying for Features
In addition to the core upgrade action elements there are additional tools available through the API that allow you to programmatically query for features within a designated scope. Using the QueryFeatures method you can query for features based on the Feature ID, the scope, features yet to be upgraded, and features by version. This can be useful in cases where you want to have more fine control over which sites receive an upgrade. More details can be found on MSDN.
Some Final Thoughts
It’s great to see the Feature Upgrade path now available and although it may mean more work and planning we have a standard and supported methodology for upgrading our Features which should help out greatly for lifecycle management of your SharePoint environment and SharePoint applications. For that I’m thankful – a good step forward in the right direction.
Saying that I still question some of Microsoft’s direction when it comes to Feature upgrades. I think it would have been more appropriate to build in a more reusable provisioning framework into the Feature framework to allow us to define more flexible actions that could be reused across features (and possibly built on by the community), beyond that of just UpgradeActions. Instead of giving us a couple of new elements that may or may not be useful for many common scenarios they could have built us something flexible so it could be adapted to be more robust if needed. I think the feature receiver is meant to address this scenario but I would have rather seen something more a kin to how MSBuild works in addition to feature receivers. Allow me to build and register a custom assembly with the feature that adds a new reusable element to the feature.xml to add new ways to customize the provisioning process. With this framework something like the UpgradeActions elements could have been a value add on top of the flexible provisioning engine. That way I wouldn’t have to wait for each release every 3 years for improvements to the Features framework.
Technorati tags: SharePoint
Technorati tags: SharePoint 2010