Upgrading Web Parts in SharePoint Foundation
Last modified: September 01, 2011
Applies to: SharePoint Foundation 2010
This topic explains the most correct and comprehensive way to upgrade Web Parts. This approach takes into account whether the Web Part is a Microsoft SharePoint Foundation, hybrid, or Microsoft ASP.NET Web Part. It also takes into account whether the Web Part is being converted from one type of Web Part to another type, what kind of upgrade action is occurring, and under what circumstances the upgrade code will or will not run. If you are creating a new version of a Web Part and need to execute upgrade logic on the property values that were stored for previous versions of the Web Part, Microsoft SharePoint Foundation 2010 provides the following options.
If your old versions are all SharePoint Foundation Web Parts, and your new version is either a SharePoint Foundation 2010 Web Part or a hybrid Web Part, implement the AfterDeserialize() method to upgrade the Web Part. This method is always called when the old version is deserialized into the new version, which is true if the Web Part is page static (that is, outside a zone), dynamic (that is, inside a zone), or instantiated through an object model call. However, the AfterDeserialize() method is called only one time when you upgrade from the old deserialized version to the new version; after your Web Part is upgraded to a hybrid Web Part, the AfterDeserialize() method is no longer called. Therefore, if you convert your SharePoint Foundation Web Part to a hybrid Web Part, and then later add some upgrade logic to the hybrid Web Part after it is deployed as a hybrid Web Part, you must implement AfterDeserialize() to handle the upgrade of a SharePoint Foundation Web Part to a hybrid Web Part 2.0. You must also implement the interfaces that are described later in this topic to handle the upgrade of a hybrid Web Part 1.0 to a hybrid Web Part 2.0. To achieve this, we recommend that you create a shared upgrade function (here, TransformMyString) that both the AfterDeserialize() method and your interface implementations call.
If the old version of the Web Part is a hybrid Web Part or an ASP.NET Web Part, and the new version is a hybrid Web Part or an ASP.NET Web Part, the AfterDeserialize() method is not called. Upgrade logic for the Web Part must be called from the OnInit() method, the EndLoad() method, and the Load() method, depending on what the upgrade must do.
For more information about how to use the AfterDeserialize() method, see Upgrading a Web Part Assembly for Microsoft SharePoint Products and Technologies and AfterDeserialize().
Detecting Property Status
Assume that you have a string property named SampleProperty, and that the format has changed so that your upgrade code must transform the contents of SampleProperty from the old format to the new format. We recommend that you create a TransformSampleProperty method that can efficiently detect whether SampleProperty has already been transformed, and if not, performs the transformation. The following example shows how to do this.
Imports Asp = System.Web.UI.WebControls.WebParts Public Class MyWebPart Inherits Asp.WebPart Private m_dirty As Boolean = False Private Sub TransformMyString() If ([SampleProperty is in the old format]) Then [Transform the SampleProperty value to the new format] m_dirty = True End If If m_dirty AndAlso Nothing IsNot WebPartManager Then SetPersonalizationDirty() m_dirty = False End If End Sub End Class
Calling TransformSampleProperty from the OnInit() method and the EndLoad() method ensures that your upgrade code is called if your Web Part is page static, dynamic, or instantiated through an object model call. It may be necessary to add an "upgraded" or "version" property to your Web Part so that TransformSampleProperty can set that property after upgrade, and so that subsequent calls to TransformSampleProperty efficiently detect whether MyString has already been upgraded. Consider using the IPersonalizable interface to store your "version" property, because this property is persisted to the store along with the rest of your Web Part properties. However, this property does not appear in the tool pane user interface (UI) when the Web Part's properties are being edited, and it does not appear in the markup for the Web Part when the Web Part is being edited in Microsoft SharePoint Designer.
In the previous example, as soon as the Web Part is deserialized, the EndLoad() method is called. Because it is true that SampleProperty is in the old format, the code for transforming the SampleProperty value runs. Because the Web Part has not been added yet to the WebPartManager, MyWebPart.WebPartManager remains null and SetPersonalizationDirty is not called. After the Web Part is added to the WebPartManager in the page, its life cycle becomes current, and MyWebPart.OnInit is called. However, it then becomes false that MyString is in the old format, and MyWebPart.WebPartManager becomes valid. Therefore, SetPersonalizationDirty is called, which triggers an automatic save of the upgraded Web Part back to the content database, regardless of the current user's rights.
If the Web Part is page static, there is no support for changing the markup in the page after the Web Part's upgrade code runs, which means that your markup always remains in its pre-upgraded state, and that your upgrade code always reruns in OnInit() whenever the page is browsed. A special case is when the Web Part tries to write back properties to the content database while the database is read-only in Microsoft SQL Server. This can occur because the Web Part upgrade is effectively deferred to first use per instance, and the first use may occur while the database is read-only. The Web Part should render but gracefully fail to save the update to the database.
Assume that you have a property X on your old version and you have renamed it to Y in your new version. In this case, implement the Load() method, and copy the value that used to be stored in X into Y, and set a dirty flag. Then, in the OnInit() method, check for the dirty flag and call SetPersonalizationDirty. The following example shows how to do this.
Imports Asp = System.Web.UI.WebControls.WebParts Public Class MyWebPart Inherits Asp.WebPart Private m_dirty As Boolean = False [Standard Web Part logic here...] Private Sub IVersioningPersonalizable_Load(unknownProperties As IDictionary) Implements IVersioningPersonalizable.Load '[Copy X into Y here…] m_dirty = True End Sub Protected Overrides Sub OnInit(e As EventArgs) If m_dirty AndAlso WebPartManager IsNot Nothing Then SetPersonalizationDirty() m_dirty = False End If End Sub End Class
In the previous example, the Load() method is called if your Web Part is dynamic or instantiated through an object model call, but this method is not called if your Web Part is page static.
The Web Part framework calls IVersioningPersonalizable interface methods regardless of a user's credentials; if your IVersioningPersonalizable code correctly marks the Web Part as dirty in the previous example, it triggers an automatic save of the upgraded Web Part back to the store by using internal code that does not check credentials. Consequently, even if a user who has limited permissions (for example, reader) is the first to browse the page, the upgrade still occurs as expected.
In the page static case, the Load() method is not called. However, you can add code in your OnInit() call to look at the Web Part's expanded properties (an ASP.NET control concept, not a Web Part framework concept), copy out the X value from the expanded properties, and assign this value to Y. In addition, in this case, there is no support for changing the markup in the page after the Web Part's upgrade code has run, which means that your markup always remains in its pre-upgraded state, and your OnInit() upgrade code always reruns whenever the page is browsed.