How does it work?
It ends up being rather simple - and perhaps that's why it wasn't considered for better documentation. (Poor excuse.) The short explanation is that:
- Your custom component binary (the DLL) is marked with a version number.
- The information that describes how your component is configured in the users' Data Flow design surface (metadata) is tagged with a version number inside their dtsx file.
- When BIDS loads the dtsx, it compares the metadata version number with the binary version. If they're different, it calls the PerformUpgrade method.
Why use it?
The PerformUpgrade method allows you to seamlessly upgrade "version 1" of your component to "version 2" without any nasty null reference exceptions when you attempt to load a metadata property that isn't there.
What that means to the user is that they don't have to delete your component from the design surface, add it again, then spend time configuring it.
How can I use it?
Using PerformUpgrade properly requires a few things. First, it requires that you identify your component's version. And NO - I do not mean the assembly version, file version, or any other version number you're used to in standard .Net development. It needs you to fill in the CurrentVersion property of the DtsPipelineComponent attribute, like this:

In this case, I've set the CurrentVersion to 2, in order to meaningfully demonstrate "upgrading" a component from version 1 to 2. You'll note that the CurrentVersion property only takes integer numbers - so no 0.9 versions are allowed!
The second thing you'll need is an override of the PerformUpgrade method - intellisense ought to start that for you nicely. Inside the PerformUpgrade function, you'll need to completely ignore the "pipelineVersion" argument - it has NOTHING to do with the version(s) of your component or metadata. It's an unused (almost undocumented) argument that may or may not get used by Microsoft at a later date. (Conspiracy theorists undoubtedly think they use it right now to make their components outperform third party ones - but anyone watching closely ought to know it isn't making a difference for them.)
You need to retrieve two version numbers - the current version of the binary component (DLL) installed on the system where the package is being designed or executed, and the version of the metadata stored in the dtsx file. I'll refer to those from here on as "binary version" and "metadata version".
You could simply hand-maintain the binary version awareness - every time you change the metadata (custom properties) of your component sufficiently, you could change the DtsPipelineComponent property by hand (which you MUST do), and then hand-code the same number in your PerformUpgrade method to let it know what the binary version is. Of course, automating that so that you only ever have to hand-maintain one version number works best. So inside your PerformUpgrade method, here's how you retrieve your binary version:

Once you have that, then you need to retrieve the version of the component's metadata that's in the dtsx file - the metadata version:

Now that you know both, you can compare them. If they're the same, you obviously need to do nothing. If your binary version is higher than the metadata version, you need to upgrade your metadata. If your metadata version is higher than the binary version, I'd recommend throwing an Exception...
I also recommend "cascading" your upgrades like this:

What does "upgrading" entail? Well, there are three scenarios. First, you could have added more capabilities or configurability to your component, requiring that the user specify more settings - in this case, you need to allocate more custom property "space" and a default value. Second, you could have deprecated (removed) a capability or configuration - in this case you could remove the custom property "space", although that's not strictly necessary (you'll just ignore those custom properties). Third, and probably most common, is that your options for your component will have changed slightly, and either the data type, or defaults will have changed - in this case you will have to remove the old properties (remembering the user's settings) and create new ones, attempting to populate them as best you can into the new framework.
If you need to add capabilities, it's pretty simple - copy any code that's "new" from your ProvideComponentProperties method, and paste it in the appropriate upgrade block. ProvideComponentProperties only gets called when a component is placed on the design surface for the first time - it's called once, and only ever once, for any instance of a component.
If you need to remove capabilities, it can be easier - do nothing. The "defunct" capabilities from the old version of the component will continue to hang around in the metadata, but if you don't read them, they're simply ignored and harmless (except for the extra bytes they take up in the metadata). To really remove them properly, you need to know the property name, and do the following:

If you fit the third scenario, where you want to change the storage type(s) and/or names of your properties, load the old values first, "convert" them into the new paradigm (if possible) or default them, then create new properties.
The very last thing you need to do in PerformUpgrade is update the metadata's version. If you don't do that, then all the work you just went to goes out the door - and you actually make things worse! The last part of your method should go something like this:

And you're done...
In Summary
- Set the CurrentVersion argument in the DtsPipelineComponent attribute of your class to the "current binary version".
- Write your PerformUpgrade method to compare ComponentMetaData.Version (metadata version) to the CurrentVersion DtsPipelineComponent attribute (runtime version).
- Remove, add, or alter the contents of your custom properties for your component.
- Set the ComponentMetaData.Version (metadata version) to the CurrentVersion DtsPipelineComponent attribute value (runtime version).
Great article Todd, thanks!
ReplyDeleteDo you know if is there a similar method for upgrading custom tasks of the control flow?
Regards...
Glad you liked the article, Jose.
ReplyDeleteI haven't done it myself, but a have a quick look at this page in Books Online: http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.dts.runtime.task.update.aspx. It refers to the CanUpdate method (click through to "Task Members" at the bottom) and the Update method.
It's marked as "deprecated" in 2008 - which means you can still use it (in 2005 and 2008), but they're obviously planning something else in the next version of SQL Server.
As far as I can tell, you need to override CanUpdate to return True, then implement your upgrading code in Update. It does seem a little messy - you have to deal with the "raw" XML of your component and "load" it into your current version component as best you can.
Hopefully, they're rearchitecting it to be "as easy" as using PerformUpgrade is for Data Flow components - although that could use a few usability tweaks too...
Good luck!
Thanks for the informative article!
ReplyDeleteDo you happen to know what the best way/practice might be to upgrade the custom component's UI?
Upgrading the UI isn't a problem. The problem here with "upgrading" is making sure that the metadata stored in the SSIS package is compatible with what your component expects. If your UI changes without changing any rules about what the metadata has to contain, then no special work is required - just deploy your new DLL.
ReplyDeleteNuts! I think the way I was deploying my UI's DLL was wrong.
ReplyDeleteYour post made it sound so easy to deploy the UI's dll - but in my case wasn't, because I always had to increment the assembly version number in order for BIDS to see the changes. Then I got to thinking, maybe I'm deploying to the GAC wrong. In my batch script (which I use to copy, uninstall & reinstall the dll), I changed the gacutil.exe /u & /i switches to /uf & /if to force my assembly regardless of previous assemblies with the same name.
Now my component's version number can stay the same and I can still tweak the UI w/o breaking existing packages. I just have to restart BIDS (which isn't too bad). Thanks Todd! Without blogs like yours, I'd never even get off the ground with this stuff.
Todd,
ReplyDeleteI'm struggling with this a bit. I have developed a custom pipeline component with 3 custom Boolean properties. Initially the default values for all 3 properties was true. All of my packages that implemented this component were working without issue in BIDS and the SSIS catalog.
I then decided I wanted to change the default value of one of the custom properties to be false. I simply changed what the default value of the property is set to in the ProvideComponentProperties method. My goal was not to change any property values of components that had been created in existing packages because they might need the value to be true or false. I just wanted the future default value to be false.
So in my PerformUpgrade method I simply set the metadata version to the binary version and did nothing else (see code below). This works perfectly in BIDS, but when my packages are executed from the SSIS catalog I still get "The component metadata could not be upgraded to the newer version of the component. The PerformUpgrade method failed."
I have tried dropping the component and re-adding it to my package, re-deploying my package from BIDS, removing the assembly from the GAC completely and re-adding it, dropping the entire project from the catalog and re-deploying, and rebooting. I don't know what else to try -- any ideas?
Much thanks in advance,
Ben
Not sure why you're getting the error, but unless you do a "real" upgrade of the metadata, then what happens to your properties may not be what you expect. A "default" setting doesn't actually get saved in the metadata. So if your Booleans were all defaulted to true, then you changed the default to false... magically when you open a package all the settings would be false.
DeleteI can't see any code in your post to comment further...
I still cannot figure it out. I have no code change, just recompile the custom component, GAC the new DLL. and got the following error no matter it is an old SSIS package or a new SSIS package.
ReplyDelete[SSIS.Pipeline] Error: The component metadata for "<***>" could not be upgraded to the newer version of the component. The PerformUpgrade method failed.
Is there anything we could manually change in the dtsx (xml) directly?
Im sure you could change the XML directly - compare an otherwise empty package made with v1 of your task against another otherwise empty package with v2.
DeleteYour PerformUpgrade may also be failing because it contains a coding error - so read your code carefully and handle exceptions...