I’ve talked about this before. When you create versions of webparts, you have two main solutions to update existing webparts :

  • Create a binding redirect within the web.config file.
  • Update each and every webpart instance of the collection site (or just the site).

The first one is really the best solution : It’s simple and very fast. But, it applies to the whole Web Application. Sometimes, the client might want to only update the site collection or even the website. In this case, we need to apply the second solution : update each webpart.

Updating each webpart requires you to create a solution that will go through all the pages of your website and replace each instance of a webpart by an other instance.

I can’t give the complete code (as it’s still not mine), but only some little hints that can help you solve this problem.

You have to analyse each an every file of your websites :

private static void AnalyseFolder( List<SPFile> list, SPFolder folder ) {
    foreach ( SPFile file in folder.Files ) {
        var fileName = file.Name.ToLower();
        if ( fileName.EndsWith( ".aspx" ) || fileName.EndsWith( ".master" ) )
            list.Add( file );
    }
    foreach ( SPFolder subFolder in folder.SubFolders )
        AnalyseFolder( list, subFolder );
}

You could restrict youself to the “pages” folder, but you would miss some pages like the layouts.

Then for each file of the class :

private void TreatPage( SPWeb web, SPFile file ) {
    try {
        if ( file.Name.EndsWith( ".master" ) ) {
            Logger.LogVerbose( "\"{0}\" is a masterpage...", file.Url );
            return;
        }
        SPWeb managerWeb = null;
        try {
            if ( file.Item == null ) {
                Logger.LogNotice( "\"{0}\" isn't part of a document library...", file.Url );
                return;
            }

            // Sometimes the code above works but this will still throw an exception
            file.Item.ToString();
        } catch ( SPException ex ) {
            Logger.LogNotice( "\"{0}\" isn't part of a document library...", file.Url );
            return;
        }
        try {
            using ( var manager = file.GetLimitedWebPartManager( PersonalizationScope.Shared ) ) {

                managerWeb = manager.Web;

                // we reload the same file but in the SPLimitedWebpartManager's web context
                file = managerWeb.GetFile( file.Item.UniqueId );

                var collection = manager.WebParts;

                var toDelete = new List<WebPart>();

                var previousWebparts = new List<WebPart>();
                foreach ( WebPart wp in collection )
                    previousWebparts.Add( wp );

                Boolean madeChanges = false;

                var toDispose = new List<WebPart>();

                foreach ( WebPart wp in previousWebparts ) {
                    Boolean deleteWebPart;

                    var newWebPart = GetNewWebPart( wp, out deleteWebPart );
                    if ( newWebPart != null || deleteWebPart ) {

                        if ( !madeChanges ) {
                            CheckOutPage( file );
                            madeChanges = true;
                        }

                        Logger.LogVerbose( "Replacing webpart \"{0}\" by \"{1}\"...", wp.GetType().FullName, newWebPart != null ? newWebPart.GetType().FullName : "- none -" );
                        if ( newWebPart != null )
                            manager.AddWebPart( newWebPart, wp.Zone != null ? wp.Zone.ID : "0", wp.ZoneIndex );

                        // it's either replaced or deleted : it will need to be removed from the page
                        toDelete.Add( wp );

                        if ( newWebPart != null )
                            toDispose.Add( newWebPart );
                    }
                }

                // If we made some changes to the file, we have to save it
                if ( madeChanges ) {
                    foreach ( var wp in toDelete )
                        manager.DeleteWebPart( wp );

                    file.Update();

                    CheckInPage( file, "Webpart update" );
                }


                // All the webparts found will need to be disposed
                toDispose.AddRange( previousWebparts );

                // We dispose everyone
                foreach ( var wp in toDispose )
                    wp.Dispose();
            }
        } finally {
            if ( managerWeb != null && managerWeb != web )
                MemoryManagement.MightDispose( web );
        }
    } catch ( Exception ex ) {
        Logger.LogException( ex, "UpdateWebpartsInPage.TreatPage( SPFile.Url=\"{0}\" )", file.Url );
    }
}