Spectacular. If you've made it this far, then you've read Part One and agreed you felt too much pain from maintaining copy-and-paste Scripts. You've said enough is enough, taken one last copy of your Script code and pasted it into Part Two's custom task harness, and massaged it to get a shiny new tool in your SSIS Control Flow toolbox.
This is the "last mile" and the place where you can change your workmanlike custom task into something that helps you get your packages designed properly, the first time, every time. (Freebies at the bottom...)
UI Choice
Those crafty developers on the SSIS team gave us two very good ways of hanging a UI on a Task. There's the completely custom way of creating your own Windows Forms interface - that's what I'm going to explain here. There's also a neat way of extending built-in SSIS classes to get a very "stock" flavour to your UI. Matt Masson does a great job detailing how to do the latter on the SSIS Team Blog: Creating a custom task with a default UI
Personally, I like the completely custom way of presenting the UI. Despite not getting the expression editor UI included for "free," you do get a ton of control over how properties are presented and interact with each other - as they tend to do. And really, it's not all that hard - the "hard" part is coding the UI, but if you've ever coded a Windows Forms app, that's a piece of cake. (I'm not going to detail that here.)
The Parts of a Custom UI
In order to construct and attach a custom UI that BIDS knows how to use, you have to follow some rules. First, you need to create a class that implements the IDtsTaskUI interface. Second, mark up your component's class attributes to tell SSIS you've created that interface implementation. Third, construct your UI class(es).
The Interface Class
The IDtsTaskUI interface describes a contract between your component and BIDS. It tells BIDS how it must interact with your component at design time when the user edits your component, and how your component is expected to respond to those requests. In order to provide an object for BIDS to communicate with using this interface, you need to define a new class that will implement your Task's handling of that interface. This may get a little confusing - because we're talking about a C# "interface" construct here (the "contract" definition), and the class we're going to be creating to implement that is going to be called the "interface class". I'll do my best to separate the two.
To get at the IDtsTaskUI interface definition, you have to include "Microsoft.SqlServer.Dts.Runtime.Design" in your "using" statements at the top of your code file, as well as "Microsoft.SqlServer.Dts.Runtime". In order to get at those, you'll need to have added references to "Microsoft.SQLServer.ManagedDTS" (you should already have that reference from constructing your Task), and "Microsoft.SqlServer.Dts.Design".
The IDtsTaskUI interface requires that you implement four methods in your class: Initialize, GetView, New, and Delete. All of them are very bare, if not empty. The key ones are Initialize and GetView. Initialize passes you references to the metadata of your Task so that you know what you're editing. GetView launches your editor.
Since Initialize and GetView are called separately, the TaskHost and IServiceProvider arguments that Initialize gets passed to it aren't available in the GetView method. And it's this GetView method that actually launches your editor. In order to handle that, you need to store the TaskHost and IServiceProvider arguments (or parts of them) inside the interface class, and pass them on to your editor class instance you create in the GetView method. Declare these at the top of your class:
private TaskHost _taskHost;
private Connections _connections;
Then place this inside your Initialize method:
this._taskHost = taskHost;
IDtsConnectionService cs = serviceProvider.GetService(typeof(IDtsConnectionService)) as IDtsConnectionService;
this._connections = cs.GetConnections();
And this inside your GetView method:
return new YourTaskEditor(this._taskHost, this._connections);
Take a peek at the template sample to get this spelled out exactly.
Decorating Your Task To Call Your UI
By default, the Task you created will tell BIDS that it has no editor, and will only be able to be configured using the Properties. In order to tell BIDS that your Task has a full editor, you need to "decorate" your Task's class with attribute values.
Open your Task class and look at the DtsTask attribute that's applied to it. You probably have a few arguments specified to that attribute, such as DisplayName and TaskType. We need to add a new UITypeName argument, and specify what it is. This argument is a string type, and it needs to specify the fully qualified assembly name of your interface class - this consists of five parts: Your full interface class name, the class name of the Task itself, the assembly version, culture, and public key token. Here's how you find all that information.
The Full Interface Class Name
In your interface class, you'll have a "namespace" statement just under the "using" statements. That's the first part of your class' full name. Then you'll see your class' name right after the "class" statement. Here's an example:
namespace Sample.SSIS.Namespace
{
class YourTaskInterface : IDtsTaskUI
{
From the above, you would get this as your full interface class name: Sample.SSIS.Namespace.YourTaskInterface
The (Short) Class Name of Your Task
This is the name of the class that you've converted your Script Task into, simple as that. As an example:
namespace Sample.SSIS.Namespace
{
[DtsTask(DisplayName = "MyTask", TaskType = "General")]
class YourTask : Task
{
From the above, you would get your Task class name as: YourTask
Assembly Version
Most likely (especially if you're working from the template), this is "1.0.0.0" - and it's typically a "bad idea" to change your assembly version - it will break (beyond automatic upgrading) any packages that used a different version of your Task.
You can find this value by opening the project properties of your Task and looking at the Application tab. Once there, press the "Assembly Information" button, and copy the "Assembly Version" value. You can also locate this value by navigating to the Windows GAC (Global Assembly Cache), finding your installed Task, and looking at the properties there.
Again, the most likely value is: 1.0.0.0
Culture
This part is also most likely set to "Neutral" - but you can check to make sure by looking at your Task DLL's properties in the GAC.
The most likely value is: Neutral
Public Key Token
This is the public half of the asymmetric key you used to sign your DLL. You may have it recorded somewhere - but you can also find this easily by looking at your Task DLL's properties in the GAC.
Tasks signed with my key have this public key token: 8b0551303405e96c
All Together Now - the Fully-Qualified UI Type Name
Now that you've located all that, string it together separated by commas, like this (taken from the above example values) and add it in to the DtsTask attribute argument list:
[DtsTask(DisplayName = "MyTask", TaskType = "General",
UITypeName = "Sample.SSIS.Namespace.YourTaskInterface,YourTask,Version=1.0.0.0,Culture=Neutral,PublicKeyToken=8b0551303405e96c")]
Your UI
This one is (almost) all up to you - use Windows Forms to design your interface. You can get as elaborate as you desire - have multiple windows, make a "wizard" style interface, use a tabbed window - anything. Your only requirement is to have a constructor that accepts a TaskHost and IServiceProvider (or just a Connections list, as I've done), as shown in the GetView method of the interface class.
In order to accomplish this from scratch, you need to add a reference to System.Windows.Forms, and place "System.Windows.Forms" in your using statements. You'll also (likely) need to add "Microsoft.SqlServer.Dts.Runtime" in your using statements in order to accept and use the TaskHost and Connections arguments you're passing your constructor.
Wrapping It Up
Again, lots of windbag explanations for something that turns out to be fairly simple. Add an argument to your DtsTask attribute, create a virtually empty "interface" class, then code your UI however you want.
Download the EmptyTaskWithUITemplate project from my SkyDrive to get started, or have a look at the multitudes of samples available on CodePlex.
No comments:
Post a Comment