这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战
翻译参考
本文主要参考翻译自 The Stairway to Integration Services
系列文章的 Advanced Event Behavior – Level 10 of the Stairway to Integration Services,目的在于对 SSIS 有一个全面清晰的认识,所有内容在原文的基础上进行实操,由于版本差异、个人疑问等多种原因,未采用完全翻译的原则,同时也会对文中内容进行适当修改,希望最终可以更有利于学习和了解 SSIS,
感谢支持!
本部分我们关注下事件行为。我们分享两种方法,来管理事件传输的默认行为(冒泡——bubbing),并介绍 父子模式(Parent-Child pattern
) 以及其中的 事件行为
关于SSIS任务事件(SSIS Task Events)
打开 "Precedence.dtsx" 包,目前控制流应该如下所示:
上一部分,我们在 "Script Task 4" 和 "序列容器1" 上创建了OnError事件处理程序 —— 称之为"侦听器"。每个 OnError事件处理程序 中我们添加一个 脚本任务 显示包含如下 SSIS变量值 的消息框:
System::ErrorCode
System::ErrorDescription
System::SourceName
在测试之前,让我们验证之前 "Precedence.dtsx" 包构建的一些设置更改。首先,点击 "序列容器1",然后按"F4"键显示属性,确认 ForceExecutionResult
属性仍旧设置为 "Success",然后点击 "Script Task 4",并按"F4"显示属性。修改 MaximumErrorCount
属性为1。
事件和执行状态(Events and Execution Status)
上面设置 "序列容器1" 的 ForceExecutionResult
属性为 "Success",并且没有改为 默认值("None")。这样的意图是演示 "序列容器1" 的 ForceExecutionResult
属性和 "序列容器1" 对错误事件响应之间的相互关系。当 ForceExecutionResult
属性覆盖了 "序列容器1" 的执行状态,它并没有干扰(not interfere
)"序列容器1"侦听和响应错误事件的能力。
当 ForceExecutionResult
设置为 "Success","序列容器1" 可能会忽略错误事件,但是这是不确定的,我们只是证明了这一点。请注意,有忽略事件处理程序的专门的方式,在本篇文章末尾我们将演示该功能。
ForceExecutionResult
属性 和 事件处理程序 之间的相互作用为数据集成开发者提供了灵活性。这一点很重要:在不使控制流程失败的情况下,错误可以发生并被侦听到(并响应)。
阻止事件冒泡
缺口处理(事件冒泡的"缺陷") —— 阻止事件冒泡
重要的是要记住,事件冒泡将导致Error事件持续沿执行堆栈向上传输。在我们的示例中,作用域的下一层是 "Precedence.dtsx" 包控制流,它代表 SSIS 包本身。
由于我们尚未更改 "Precedence.dtsx" 包的 MaximumErrorCount 或 ForceExecutionResult 属性,因此当错误冒泡到包容器时,该包将失败。
这可能是,也或者不是预期的行为。
我们面临一个设计决定:
-
A —— 在我们想要捕获错误的任务之上的每个作用域级别上增加容错能力,从而为整个SSIS包增加容错能力(无论是否需要)。
-
B —— 我们可以在"Script Task 4"中中断错误事件(
Error event
)默认的冒泡行为。
让我们看下选项B。
返回 "Script Task 4" 的OnError事件处理程序,点击顶部的SSIS下拉菜单并点击"变量"(Variables
)显示变量窗口。"网格选项"中设置显示"系统变量"。
"System::Propagate" 变量是一个 Boolean 变量,默认为True。"System::Propagate"是事件冒泡的控制变量。
点击 "值"(Value) 列改变默认值 True 为 False。
调试器中执行 "Precedence.dtsx" SSIS包,出现 "Script Task 4" 的提示框时点击 "否"(No)。"Script Task 4" 的 OnError事件处理程序 执行:
随后,"序列容器1" 的 OnError事件处理程序 没有执行,并且 "Script Task 3" 和 "序列容器1" 执行完成。
"Script Task 4" 引发的错误事件被 "Script Task 4" 的OnError事件处理程序听到,但是并没有冒泡到 "序列容器1" 的OnError事件处理程序。
编程控制(Programmatic Control)
我们可以在脚本任务中编程控制事件冒泡。
在 "Script Task 4" 的 OnError事件处理程序 中打开脚本任务编辑器,在 "ReadWriteVariables" 属性中添加 "System::Propagate" 变量。
点击"编辑脚本"按钮打开.Net
脚本编辑器,在 Main
函数中添加如下的if语句:
根据 "System::ErrorCode" 变量的值,添加条件逻辑控制"System::Propagate"变量的值。
在执行 "Precedence.dtsx" SSIS包测试之前,修改 "Script Task 4" 的OnError事件处理程序中 "System::Propagate" 变量的默认为原来的True。
通过按"F5",在调试器中运行 “Precedence.dtsx” 包,在 "Script Task 4" 的提示框中点击"否"。可以观察到 "Script Task 4" 的OnError事件处理程序的 "脚本任务" 执行并显示错误事件的详细信息。
后面同样执行完 “Script Task 3” 和 “序列容器1”,错误事件并没有向上冒泡。
事件冒泡已经通过编程进行管理,我们已经在SSIS中实现了动态容错(dynamic fault tolerance
)。
自定义错误
我们可以通过更改 "Script Task 4" 引发的 ErrorCode 值来进行测试。
在“控制流”上,打开 "Script Task 4" 编辑器,然后单击“编辑脚本”按钮打开脚本编辑器。在 if 代码块中编辑代码以响应消息框按钮的单击,如下图所示,可以看到 Dts.Events.FireError 方法的智能提示。
注释掉 Dts.TaskResult = (int)ScriptResults.Failure;
添加:
Dts.Events.FireError(-1001, sTaskName, "Script Task 4执行失败!", "", 0);
复制代码
当前代码修改用户点击 "Script Task 4" 询问成功的提示框中 "否"按钮 时的响应,为其生成一个 自定义的错误代码和错误描述(custom error code and error description
) —— -1001
和 Script Task 4 failed!
。
Dts.Events.FireError
方法的参数列表分别是:ErrorCode, SubComponent, Description, HelpFile 和 HelpContext。
调试运行 "Precedence.dtsx" 包。在 "Script Task 4" 的消息提示框中点击"否","Script Task 4" 的OnError事件处理程序中显示消息框变为如下所示:
因为 “Script Task 4” 的 OnError事件处理程序 中 "脚本任务" 的代码仅仅当 "System::ErrorCode"变量值 为 6 时才会设置 "System::Propagate" 为False。所以,当前事件仍会冒泡到"序列容器1"的事件处理程序。
此处演示仅仅在 if 条件语句中隔离单个ErrorCode值。实际中可以根据需要使用其他条件语句,如 C#
中的 Switch
或 VB 中的 Select Case
。甚至可以使用SSIS工具箱中的其他选项。
冒泡++
事件冒泡可以扩展到单个SSIS包的范围之外。为了检查事件的这种行为,我们必须花一点时间介绍“父子”SSIS设计模式(Parent-Child SSIS design pattern
)
父子SSIS设计模式——Parent-Child SSIS Design Pattern
SSIS执行包任务 用于从 另一个SSIS包 调用 SSIS包。
当一个包调用另一个包时,调用的包(包含"执行包任务"(Execute Package Task
)的包)称为父包(parent package
),而被"执行包任务"调用的包称为子包(child package
)。事实证明,这种描述不仅仅是语义的,也很好地描述了程序包之间的一些有趣行为和交互。
设计父子包
在 “FirstSSIS” 项目解决方案中添加一个新的 SSIS 包。
在解决方案资源管理器中,右击 "SSIS包" 虚拟文件夹,点击 "新建SSIS包"(New SSIS Package
)。
一个名为 "Package1.dtsx" 的新包被创建添加到解决方案,右击该包并点击 "重命名",修改新包的名字为"Parent.dtsx"。
打开 "Parent.dtsx" 包,拖拽一个 "执行包任务"(Execute Package Task
) 到控制流中。
双击 "执行包任务" 打开执行包任务编辑器。
"执行包任务"(
Execute Package Task
) 用来执行 另一个SSIS包。另一个SSIS包可以文件形式存储,也可以存储在 SQL Server 中。在"执行包任务编辑器"中需要配置"子包"的运行方式,指定如何引用子包及哪个子包(子包的位置)等。
执行包任务编辑器中添加子包有两种引用方式:项目引用(Project Reference
)和外部引用(External Reference
)。项目引用可以很方便的引用当前解决方案下的其他包。外部引用用来引用存储在其他位置(文件系统File system
或 SQL Server)的包
因为我们要启动当前解决方案下的另一个SSIS包,因此在"项目引用"下,"PackageNameFromProjectReference" 属性选择 "Precedence.dtsx" 包。
点击确定,关闭“执行包任务编辑器”。当在调试器中执行 "Parent.dtsx" 包时,它将会调用 "Precedence.dtsx" 包。
SSIS的布局调整父子包一起显示
在调试器中执行 SSIS 包之前,我想向您展示一个方便的技巧(a handy trick
),以测试参与 父子SSIS设计模式 的SSIS包。
确保 Parent.dtsx
和 Precedence.dtsx
都打开。左击并抓住 "Precedence.dtsx" 标签,然后向右拖动使其离开原有标签,然后拖动到显示出来的布局线到如下图中的位置,释放鼠标左键。
最后,使父子模式中的两个包显示如下:
执行包及父子间的错误事件处理
点击 "Parent.dtsx" 包的控制流的空白区域,使 "Parent.dtsx" 选中。按"F5"键执行 "Parent.dtsx" 包 —— 它将会调用 "Precedence.dtsx"。
"Script Task 2" 提示框中点 "是","Script Task 4"提示框中点击"否"。确认 "Script Task 4" 的 OnError事件处理程序 和 "序列容器1" 的 事件处理程序 中"脚本任务"生成的 OnError事件处理消息框,确认"Script Task 3"的完成提示。
执行完成后应该如下图所示:
在 "Precedence.dtsx" 中,"Script Task 4"失败 和 "序列容器1"成功。"Parent.dtsx" 中执行包任务失败。这种情况是因为"序列容器1"的ForceExecutionResult
属性保留着"Success"设置,一个错误事件冒泡穿过"序列容器1"。
如果没有更多的开发尝试,我们将无法观察到发生的其他情况 —— 比如 "Precedence.dtsx" 包执行失败。下面,向 "Precedence.dtsx" 包添加一个 OnError事件处理程序 进行演示。
复制 "序列容器1" 的 OnError事件处理程序 的 "脚本任务",导航到 "Precedence"包 的 OnError事件处理程序,点击"链接"创建它,然后粘贴"脚本任务"到这。
打开"脚本任务编辑器",然后点击"编辑脚本"按钮。找到 "sSubComponent" 定义的位置。修改为如下代码:
var sSubComponent = "Precedence Package OnError Event Handler";
复制代码
点击 "Precedence.dtsx" 的控制流的空白区域,确保它被选择。按 "F5" 键执行 "Precedence.dtsx" 包,"Script Task 4" 提示框中点击 "No",当 OnError事件处理程序 执行和显示消息框时,观察 "Precedence" 的 OnError事件处理程序,如下:
复制 Precedence 的 OnError事件处理程序 中的 "脚本任务",导航到 "Parent" 包的 OnError事件处理程序,创建 事件处理程序 并粘贴 "脚本任务"。
同样,打开脚本任务编辑器,打开"编辑脚本"按钮,修改 "sSubComponent" 的初始化。
var sSubComponent = "Parent Package OnError Event Handler";
复制代码
点击 "Parent.dtsx" 包控制流的空白区域,确保 "Parent.dtsx" 被选中。按"F5"键执行 "Parent.dtsx" 包。和之前的操作一样,"Script Task 4"提示框中点击"否"。在确认 "Script Task 4"、"序列容器1"、"Precedence"包 的 OnError事件处理程序 的消息框后,"Parent"包的OnError事件处理程序接下来会执行。
这里有几点有趣的事需要指出:
首先,在 父子SSIS设计模式 中,错误事件从 "Precedence.dtsx" SSIS包的 "底部"(Script Task 4),冒泡到"Parent.dtsx" SSIS包的 "顶部"。
Precedence.dtsx
包就好像在Parent.dtsx
包的“执行包任务”的“作用域内”一样。
第二,最初由 "Script Task 4" 生成的错误事件(Error event)仍然配置为最初的变量值。当 Error事件 冒泡时,ErrorCode,ErrorDescription和SourceName SSIS变量的值保持静态 —— 即使它从子包冒泡到父包。
这种行为适用于所有的SSIS任务事件,不仅仅OnError事件。
最后一件事(DisableEventHandlers属性)
你可能会考虑,在父子SSIS设计模式中,我们需要在子级水平管理事件吗?我们可以仅仅在父级水平管理所有事件吗?
答案是:它取决于是否适合你公司业务的事件管理设计,即使你允许大多数事件冒泡到父包的顶部,也存在有效的用例,可以在子包范围内捕获单个SSIS任务和容器事件。我相信,关键是你现在对配置这些事件的灵活性有了更多的了解。你现在有了可选项,并且所有的选择都很好。
我想讨论的最后一件事是 DisableEventHandlers
属性。
SSIS中的可执行文件 —— 换句话说是 SSIS任务和容器 —— 都有 DisableEventHandlers
属性。
DisableEventHandlers
属性由任务或容器范围内的可执行文件继承。其默认值为False。不是所有的属性都是true,这一点很重要。另外 MaximumErrorCount 和 ForceExecutionResult 是不继承的。
点击 "Precedence.dtsx" 控制流,按"F4"显示包属性,修改 DisableEventHandlers
属性为True。
点击 "Parent.dtsx" 包控制流的空白区域,确保"Parent.dtsx"被选中。按"F5"执行"Parent.dtsx"包。同样, "Script Task 4" 提示框中点击"否"按钮。然后,下一个显示的是 "Parent" 的 OnError事件处理程序 的消息框。
为什么?因为 "Precedence.dtsx" SSIS包的事件处理程序现在是禁用的。
同样,你可能希望在 子包事件处理程序内隔离某些事件并停止冒泡。
总结
在本文中,我们关注于事件的行为。并演示了两种方法来更改事件冒泡的默认行为:
-
使用
DisableEventHandlers
属性禁用事件处理程序; -
使用脚本任务在事件处理程序内操纵
System::Propagate
SSIS变量的值,取消事件冒泡。
同时,介绍了简单的自定义错误,以及父子模式,检查了父子模式内的事件行为。
注:以上 "Precedence.dtsx" 的包名 "Package1",可通过包属性中的 Name 属性进行修改,使其名字和"Precedence"对应。