自定义 SSIS 控制流组件






4.36/5 (6投票s)
为 SQLServer 2005 Integration Service 创建自定义控制流组件
引言
本文面向有Web或桌面应用程序开发经验,并且开始开发自定义组件的开发者。最近搜索关于创建SQLServer 2005集成服务(SSIS)自定义控件的教程,发现可用的主题非常少。我能找到的最好的例子是Microsoft的下载中心这里。然而,这些例子除了简短的代码注释外,几乎没有进一步的解释,并且未能涵盖到一个新手控件开发者(我)可能会遇到的陷阱。本文旨在回顾一些陷阱——见下文的关注点——并解释实现的解决方案。
背景
最近的一个Web应用程序项目,每个月都有相对大量的批量处理需求。大约有30多个DTSX包在用户可以访问Web应用程序之前运行,用于加载和处理数据。在运行这些进程时,有必要阻止访问Web UI。这通过数据库中的两个表JobsHeader和JobDetails,以及一个根据需要进行编辑的存储过程来实现。考虑到需要管理包中的重复条目,显然创建一个自定义控件是最佳解决方案。
SQLJobTask
正如大多数优秀的程序员一样,我试图寻找一个现有的例子来使用,而不是重新发明轮子。我在SSIS编程示例中找到了一个很好的例子:SQLConnectionManager
,它是前面提到的下载中的一个示例。自定义控件分为两部分。
- 用户界面
- 任务
用户界面
通过选择的控制网站访问方法,实际上只需要两个值:programId
,以及它是开始还是停止。其他信息在其他区域使用,但在这里也很合适。在构建UI时,有两个类:表单本身和UI逻辑。
表单
创建表单遵循标准流程。这里唯一新增的是TaskHost
对象的使用。
#region Private members
private TaskHost _taskHost;
#endregion
TaskHost
是你正在开发的任务的容器。这是用户输入如何从表单传递到任务的方式。表单可以填充TaskHost.Properties
...
public SQLJobTaskForm(TaskHost taskHost)
{
InitializeComponent();
this._taskHost = taskHost;
// Initialize TextBox controls with the values from the Task.
// These properties are accessed through the task's TaskHost object.
this.txtProgId.Text =
this._taskHost.Properties[Constants.PROG_ID].GetValue(
taskHost).ToString().Trim();
.
.
.
}
...或者它可以使用TaskHost.Properties
来初始化表单中的控件。
private void btnOK_Click(object sender, EventArgs e)
{
// Set the properties of the task through the TaskHost object.
this._taskHost.Properties[Constants.ENABLE_SW].SetValue(
this._taskHost, this.chkEnabled.Checked);
.
.
.
}
UI逻辑
UI逻辑相对简短。它负责将TaskHost
对象从任务传递到表单。这在IDtsTaskUI
接口的两个方法中完成。
namespace DMOE.DTS
{
public sealed class SQLJobTaskUI : IDtsTaskUI
{
private TaskHost _taskHostValue;
public void Initialize(TaskHost taskHost,
IServiceProvider serviceProvider)
{
// Store the TaskHost of the task.
// This will need to be passed to the form.
this._taskHostValue = taskHost;
}
public System.Windows.Forms.ContainerControl GetView()
{
return new SQLJobTaskForm(this._taskHostValue);
}
.
.
.
}
}
IDtsTAskUI
接口中还有其他方法。但是,在这个控件中不需要它们。正如所见,SQLJobTaskUI
用两个参数初始化。它们是TaskHost
(我们已经提到的容器)和IServiceProvider
。GetView()
方法将TaskHost
对象传递给新表单对象的实例化。
任务
该控件继承自SSIS控件流对象类别中的SQLTask
对象,并执行大部分工作。令人惊讶的是,这里的复杂性降低了。
namespace DMOE.DTS
{
[
Guid("FF12F4D7-7987-43cf-A5C8-2C31713C2B01"),
DtsTask(DisplayName="SQLJobTask",
TaskType="ExecuteSQLTaskDMOEJobs",
IconResource = "DMOE.DTS.SQLJobTask.SQLJobTask.ico",
UITypeName = "DMOE.DTS.SQLJobTaskUI,DMOE.DTS.SQLJobTask," +
"Version=1.0.0.0,Culture=Neutral,PublicKeyToken=06021987c5ebca3a"
)
]
public sealed class SQLJobTask : Task, IDTSComponentPersist
{
#region .Ctor
public SQLJobTask()
{
}
#endregion
#region Properties
#region ProgramId
private string _progID = string.Empty;
public string ProgramId
{
set { this._progID = value.Trim(); }
get { return this._progID; }
}
#endregion
.
.
.
}
}
如您所见,来自UI表单的值将存储在字段中。这里只需要很少的代码。TaskHost.Properties
由表单逻辑设置。您只需要记住属性名称是区分大小写的,并且通过TaskHost.Properties[index]
访问。这个索引可以是字符串、索引或标识。我选择使用字符串,并将值保存在Constanst.cs中。存储过程的调用是在运行时发生的重写的Execute()
方法中完成的。
关注点
首先,创建您自己的强命名文件。您需要这个文件才能将其添加到GAC,并在源代码中用这个新的公钥令牌替换公共令牌。网上有许多文章回顾了操作方法。有关部署的信息,请参阅下面的参考文献。UI由任务类中的DtsTask
元数据引用。
DtsTask(DisplayName="SQLJobTask",
TaskType="ExecuteSQLTaskDMOEJobs",
IconResource = "DMOE.DTS.SQLJobTask.SQLJobTask.ico",
UITypeName = "DMOE.DTS.SQLJobTaskUI,DMOE.DTS.SQLJobTask," +
"Version=1.0.0.0,Culture=Neutral,PublicKeyToken=06021987c5ebca3a"
UITypeName
告诉任务调用哪个UI。请注意,UI程序集和任务程序集都列出了,以及从强命名文件中检索到的公钥令牌。注意IconResource
,它提供了图标文件名。我不想使用系统分配的默认图标。
此时,该组件就可以部署并在SSIS IDE中运行了。在SSIS SQL Server Management Studio中部署并运行该组件后,该组件无法工作。programId
(唯一必需的信息)未在设计时保存。花了一点时间才意识到我没有持久化属性。一旦我意识到这一点,答案就相当简单了。该包了解XML文档和IDTSInfoEvents
。
public void LoadFromXML(XmlElement node, IDTSInfoEvents infoEvents)
{
// This might occur if the task's XML has been modified outside of
// the Business Intelligence
// Or SQL Server Workbenches.
if (node.Name != Constants.TASK_NAME)
{
throw new Exception(string.Format(
"Unexpected task element when loading task - {0}.",
Constants.TASK_NAME));
}
else
{
// let error bubble up
// populate the private property variables with values from
// the DTS node.
this._progID = node.Attributes.GetNamedItem(Constants.PROG_ID).Value;
.
.
.
}
}
public void SaveToXML(XmlDocument doc, IDTSInfoEvents infoEvents)
{
//create node in the package xml document
XmlElement taskElement =
doc.CreateElement(string.Empty, Constants.TASK_NAME, string.Empty);
// create attributes in the node that represent the
// custom properties and add each to the element
// ProgramId property
XmlAttribute xaProgID = doc.CreateAttribute(string.Empty,
Constants.PROG_ID, string.Empty);
xaProgID.Value = this._progID;
taskElement.Attributes.Append(xaProgID);
.
.
.
//add the new element to the package document
doc.AppendChild(taskElement);
}
需要涵盖的信息量——例如部署和调试自定义组件——可能令人望而生畏,并且超出了本文的范围。更多信息,请参阅以下参考文献。
参考文献
- 构建、部署和调试自定义组件:Microsoft的一篇优秀文章。
- Professional SQL Server 2005 Integration Services by Brian Knight, et al:Wrox的另一本优秀资源。第14章介绍了创建和部署不同类型的自定义组件。有关构建和部署的信息尤为重要。
历史
- 2007年5月25日 -- 初始发布
- 2007年6月22日 -- 文章经过编辑并移至CodeProject.com主文章库