本篇文章是Integration Services系列的第十篇,详细内容请参考原文。
简介
在前一篇, we introduced fault tolerance by examining methods of task execution state management using the MaximumErrorCount and ForceExecutionResult properties.我们还学习了SSIS控制流任务错误、事件处理程序和容器之间的关系。
这一篇我们重点关注事件的行为。在这一篇,我们分享两种用于处理默认事件传输行为(bubbling)的方法。我们还会引入父子模式并演示父子模式中的事件行为。
SSIS任务事件
打开Precedence.dtsx包。你的控制流面板应该如图10.1所示:
图10.1
在第九篇,我们关注在序列容器1.我们在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.
在我们做更多修改前,通过按F5在BIDS调试器下执行Precedence.dtsx包。对于"Succeed Script Task 2?"的选择不会影响演示的目的,但是现在以及这篇文章剩余部分我们会选择"是"按钮(图10.2)引起 Script Task 2成功。
图10.2
当提示"Succeed Script Task 4?"时,选择否按钮从Script Task 4引起一个错误事件:
图10.3
当Script Task 4产生一个错误事件,在第九篇中配置的OnError事件处理程序监听错误事件,然后执行,显示类似图10.4所示的消息对话框:
图10.4
但是错误事件并不会在这里停止,它会继续向上层冒泡,将执行结果传送到下一级:序列容器1,它包含Script Task 4.我们可以从序列容器1配置的OnError事件处理程序上看到类似图10.5的消息对话框:
图10.5
一旦我们接受这个消息对话框,另一个显示Script Task 3完成的对话框会出现。接受Script Task 3的消息,让Precedence.dtsx包完成执行,最终如图10.6所示:
图10.6
注意序列容器1成功。或者,至少报告成功?
事件和执行状态
第九篇我们设置序列容器1的ForceExecutionResult属性为"Success"并且没有把它修改回默认设置("None")。这是故意用来演示序列容器1的ForceExecutionResult属性与序列容器1对错误事件的响应之前的相互作用。ForceExecutionResult属性重写序列容器1的执行状态,它不会妨碍序列容器1对错误事件的监听能力。当ForceExecutionResult属性设置为"Success"时,序列容器1好像会忽略错误事件,但那是不准确的,我们刚刚证明它。请记住有一种方法可以忽略事件处理程序,我们将在这一篇的最后显示这项功能。事件处理程序和ForceExecutionResult属性之间的相互作用为数据集成开发提供灵活性,有利于容错。这是非常重要的:一个错误可以发生,并且被监听到,但是不会导致控制流失败。
Kicking it Down a Notch
It is important to remember that event bubbling will cause the Error event to continue transmission up the execution stack.在我们的例子,下一级就是Precedence.dtsx包控制流代表SSIS包它自己。
因为我们没有修改Precedence.dtsx包的MaximumErrorCount和ForceExecutionResult属性,当错误冒泡到包容器这个包会失败。
这可能是/可能不是期望的行为。我们面临一个期望决定。我们可以A)在每一个想捕获错误的任务范围级添加容错属性——从而为整个包添加容错属性(不管我们期望与否);B)我们可以打断错误事件的默认冒泡行为。让我们查看选项B.
返回到Script Task 4的OnError事件处理程序,通过点击SSIS下拉菜单中的变量(图10.7),显示变量窗口:
图10.7
一旦打开变量窗口,点击显示系统变量按钮。变量System::Propagate是一个布尔型的变量,默认值是True.如图10.8所示,System::Propagate是事件冒泡控制变量:
图10.8
点击值列将默认值从True修改为Fales,如图10.9所示:
图10.9
在BIDS调试器下执行Precedence.dtsx包。Script Task 2选择是,Script Task 4选择否。Script Task 4的OnError事件处理程序执行如图10.10所示:
图10.10
图10.11显示Script Task 3正在执行,序列容器1的OnError事件处理程序没有执行:
图10.11
Script Task 4产生的错误事件被Script Task 4的OnError事件处理程序监听到,但是它没有冒泡到序列容器1的OnError事件处理程序。
Programmatic Control
We can programmatically control event bubbling inside a Script Task.为了演示,打开Script Task 4的OnError事件处理程序中的脚本任务,将System::Propagate变量添加到ReadWriteVariables属性,如图10.12所示:
图10.12
点击编辑脚本按钮,编辑Public Sub Main()中的代码,在56行添加代码10.1中的IF/Then语句,如图10.13所示:
图10.13
Public Sub Main() Dim iErrorCode As Integer = _ Convert.ToInt32(Dts.Variables("ErrorCode").Value) Dim sErrorDescription As String = _ Dts.Variables("ErrorDescription").Value.ToString Dim sSourceName As String = _ Dts.Variables("SourceName").Value.ToString Dim sSubComponent As String = _ "Script Task 4 OnError Event Handler" Dim sMsg As String = "Source: " & sSourceName & vbCrLf & _ "Error Code: " & iErrorCode.ToString & _ vbCrLf & _ "Error Description: " & _ sErrorDescription MsgBox(sMsg, , sSubComponent) If iErrorCode = 8 Then Dts.Variables("Propagate").Value = False Else Dts.Variables("Propagate").Value = True End If Dts.TaskResult = ScriptResults.Success End Sub
代码10.1
代码添加条件逻辑控制System::Propagate的值,它依赖于System::ErrorCode变量的值。在测试执行Precedence.dtsx包前,修改System::Propagate变量的值为True,如图10.14所示:
图10.14
设置System::Propagate为True,就是启用事件冒泡。如果OnError事件没有冒泡,那是因为我们将System::Propagate的值从True设置为False.在BIDS调试器下执行Precedence.dtsx包,Script Task 2选择是,Script Task 4选择否。
观察Script Task 4的OnError事件处理程序,我们看到脚本任务执行并显示错误事件明细,包括错误代码,如图10.15所示:
图10.15
图10.16说明在脚本代码中我们成功将System::Propagate值从True(执行包前我们修改的默认值)修改为False,因为Script Task 3执行但是序列容器1的OnError事件处理程序没有执行:
图10.16
事件冒泡以编程方式管理并且我们在SSIS实现动态容错。
我们可以通过修改Script Task 4引起的错误代码的值来测试。在控制流面板,打开Script Task 4编辑器,点击编辑脚本按钮打开脚本编辑器。我会修改响应消息对话框按钮点击的If/Then代码块(注释第48行,添加第49-50行),如图10.17所示:
图10.17
Public Sub Main() Dim sTaskName As String = Dts.Variables("TaskName").Value.ToString Dim iResponse As Integer iResponse = MsgBox("Succeed " & sTaskName & "?", _ MsgBoxStyle.YesNo + MsgBoxStyle.Question, _ sTaskName & " Success Question") If iResponse = vbYes Then Dts.TaskResult = ScriptResults.Success Else 'Dts.TaskResult = ScriptResults.Failure Dts.Events.FireError(-1001, "Script Task 4", _ "Script Task 4 failed!", "", 0) End If End Sub
代码10.2
代码修改了用户对"Succeed Script Task 4?”点击否按钮的响应。Script Task 4现在产生一个自定义的错误编码和错误描述:-1001和"Script Task 4 failed!"。Dts.Events.FireError方法的参数列表有:ErrorCode,SubComponent,Description,HelpFile and HelpContext.
为了测试,在BIDS调试器下按F5执行Precedence.dtsx包。Script Task 2选择是,Script Task 4选择否。观察Script Task 4的OnError事件处理程序,我们看到消息对话框显示的错误事件属性和之前的不一样,如图10.18所示:
图10.18
因为只有当System::ErrorCode变量的值等于8时,Script Task 4的OnError事件处理程序的脚本任务中的代码才会将System::Propagate设置为False,这个事件会冒泡到序列容器1的OnError事件处理程序,如图10.19所示:
图10.19
在这个演示中,我们在Script Task 4的OnError脚本中使用一个If/Then条件语句处理ErrorCode的值。你也可以使用其他的条件语句,比如VB中的Select Case和C#中的Switch.You have additional options because event handlers expose the Control Flow toolbox.
Bubble++
事件冒泡可以超出一个单一SSIS包的域。为了检测这种事件行为,我们需要介绍父子SSIS设计模式。
父子SSIS设计模式
SSIS执行包任务用于从一个SSIS包调用另一个SSIS包。当一个包调用另一个包时,调用者(包含执行包任务)当作父包,被执行包任务调用的包当作子包。It turns out this description is more than merely semantics; it provides a good description of some interesting behavior and interaction between the packages.
让我们添加一个新SSIS包到My_First_SSIS_Project解决方案。在解决方案资源管理器,右击SSIS包虚拟文件夹,选择新建SSIS包,如图10.20所示:
图10.20
一个新包创建并添加到解决方案。因为这个解决方案已经包含名为Package.dtsx的SSIS包,新包被命名为Package1.dtsx,右击Package1.dtsx选择重命名,将新SSIS包命名为Parent.dtsx,如图10.21所示:
图10.21
当你按回车键,会被提醒是否要同时重命名包对象?如图10.22所示:
图10.22
通常点击"是"按钮。解决方案资源管理器下显示的SSIS包代表两个对象:系统文件夹下的一个文件和解决方案下的一个包对象。当你重命名一个包时,首先更改文件的名称,然后提醒你是否也要重命名包对象。
你现在已经有一个名为Parent.dtsx包在你的SSIS解决方案,如图10.23所示:
图10.23
拖动一个执行包任务到Parent.dtsx包的控制流面板,如图10.24所示:
图10.24
双击执行包任务打开执行包任务编辑器,因为我们打算启动一个存储在文件系统中的SSIS包,修改Location属性为文件系统,如图10.25所示:
图10.25
现在我们需要配置一个与文件系统中Precedence.dtsx包的SSIS文件连接管理器。点击连接属性下拉菜单选择"新建连接…",如图10.26所示:
图10.26
当打开文件连接管理器编辑器后,点击浏览打开选择文件窗口。导航到Precedence.dtsx文件位置。如果你导航到包含你的SSIS解决方案(My_First_SSIS_Project)的文件夹,你会发现Precedence.dtsx文件的位置在...My_First_SSIS_ProjectMy_First_SSIS_ProjectPrecedence.dtsx如图10.27所示:
图10.27
点击Precedence.dtsx文件,然后点击打开按钮。这会返回到文件连接管理器编辑器并在文件文本框显示包文件的完整路径,如图10.28所示:
图10.28
点击确定按钮完成文件连接管理器的创建,返回到执行包任务编辑器,如图10.29所示:
图10.29
点击确定按钮关闭执行包任务编辑器。当我们在BIDS调试器下执行Parent.dtsx包时,它会调用Precedence.dtsx包。在执行包前,让我们将Parent.dtsx和Precedence.dtsx包都打开,它们应该类似图10.30:
图10.30
左键按住Precedence.dtsx页签,并将它往操作面板拖动,当你看到一个小页面时放开左键,你就会被提示新建水平或垂直选项卡组。选择新建垂直选项卡组,如图10.31所示:
图10.31
BIDS会调整包如图10.32所示,让接下来的父子执行更方便:
图10.32
点击Parent.dtsx包的控制流面板的空白区,确保选择了Parent.dtsx.按F5执行Parent.dtsx包,会调用Precedence.dtsx,Script Task 2选择是,Script Task 4选择否。接受Script Task 4和序列容器1的OnError事件处理程序产生的事件处理程序消息。接受Script Task 3提醒的完成消息对话框。执行应该完成并且BIDS应该如图10.33所示:
图10.33
在Precedence.dtsx,注意Script Task 4失败,序列容器1成功。在Parent.dtsx,注意执行包任务失败。因为序列容器1的ForceExecutionResult属性保持设置为"Success"。一个错误事件冒泡通过序列容器1。如图10.34所示:
图10.34
Something else happened that we cannot observe without more development effort-Precedence.dtsx包失败。让我们为Precedence.dtsx添加一个OnError事件处理程序。从序列容器1的OnError事件处理程序拷贝脚本任务。导航到Precedence包OnError事件处理程序,点击链接创建,然后粘贴脚本任务,如图10.35所示:
图10.35
打开脚本任务,点击编辑脚本按钮打开脚本编辑器。用代码10.3编辑第46行代码,如图10.36所示:
图10.36
Public Sub Main() Dim iErrorCode As Integer = _ Convert.ToInt32(Dts.Variables("ErrorCode").Value) Dim sErrorDescription As String = _ Dts.Variables("ErrorDescription").Value.ToString Dim sSourceName As String = _ Dts.Variables("SourceName").Value.ToString Dim sSubComponent As String = _ "Precedence Package OnError Event Handler" Dim sMsg As String = "Source: " & sSourceName & vbCrLf & _ "Error Code: " & iErrorCode.ToString & _ vbCrLf & _ "Error Description: " & _ sErrorDescription MsgBox(sMsg, , sSubComponent) Dts.TaskResult = ScriptResults.Success End Sub
代码10.3
点击Precedence.dtsx包的控制流面板的空白区,确保选择了Precedence.dtsx.按F5执行包。Script Task 2选择是,Script Task 4选择否。观察Precedence的OnError事件处理程序执行和显示的消息对话框,如图10.37所示:
图10.37
拷贝Precedence的OnError事件处理程序到剪切板,导航到Parent包的OnError事件处理程序,创建并粘贴脚本任务,如图10.38所示:
图10.38
打开脚本任务编辑器,点击编辑脚本按钮打开脚本编辑器,用代码10.4编辑第46行代码,如图10.39所示:
图10.39
Public Sub Main() Dim iErrorCode As Integer = _ Convert.ToInt32(Dts.Variables("ErrorCode").Value) Dim sErrorDescription As String = _ Dts.Variables("ErrorDescription").Value.ToString Dim sSourceName As String = _ Dts.Variables("SourceName").Value.ToString Dim sSubComponent As String = _ "Parent Package OnError Event Handler" Dim sMsg As String = "Source: " & sSourceName & vbCrLf & _ "Error Code: " & iErrorCode.ToString & _ vbCrLf & _ "Error Description: " & _ sErrorDescription MsgBox(sMsg, , sSubComponent) Dts.TaskResult = ScriptResults.Success End Sub
代码10.4
点击Parent.dtsx包的控制流面板的空白区,确保选择了Parent.dtsx.按F5执行Parent.dtsx包,会调用Precedence.dtsx,Script Task 2选择是,Script Task 4选择否。接受Script Task 4、序列容器1和Precedence包的OnError事件处理程序产生的事件处理程序消息。注意接下来Parent包的OnError事件处理程序的执行,如图10.40所示:
图10.40
这里我会指出几件有趣的事。首先,在父子SSIS设计模式,错误事件从Precedence.dtsx包的底部(Script Task 4)冒泡到Parent.dtsx包的顶部。Figure 10.41 shows my “artist’s concept” of what is really and truly happening here.The Precedence.dtsx package acts as if it is "in scope" of the Parent.dtsx package’s Execute Package Task——仿佛它是执行包任务中的一个可执行文件虚拟文件夹。在真实环境,你不可能看到图10.41所示关系的包资源管理器。执行包任务不包含一个可执行文件虚拟文件夹,and if they did you would not see the Package Explorer from the child package included therein (at least not in a current version of SSIS at the time of this writing). 图形代表父子SSIS设计模式的事件冒泡行为。
图10.41
其次,错误事件,Script Task 4最初产生,一直保持原始变量的值。错误代码、错误描述、源保持静态。哪怕它从子包冒泡到父包,如图10.42所示:
图10.42
最后一件事
我们需要在父子SSIS设计模式的子级管理事件吗?Can’t we just manage them all at the parent level?The answer is,"It depends."If that behavior fits your enterprise event management design, then yes.但是也有案例是在子包范围捕获个别SSIS任务和容器事件,即使你允许大部分事件冒泡到父包的顶部。The key,I believe,is now you know more about the flexibility you have in configuring these events.
这一篇最后要讨论的事情是DisableEventHandlers属性。SSIS中的可执行文件(SSIS任务、容器)都有DisableEventHandlers属性。DisableEventHandlers属性被任务或容器范围内的可执行文件继承。这是一个重要的区别,因为不是所有的属性都是这样的。例如MaximumErrorCount和ForceExecutionResult属性就不会继承。为了演示,点击Precedence.dtsx控制流然后按F4显示包属性,修改DisableEventHandlers属性为True(此时序列容器、容器中的任务都会继承DisableEventHandlers属性为True),如图10.43所示:
图10.43
点击Parent.dtsx包的控制流面板的空白区,确保选择了Parent.dtsx.按F5执行Parent.dtsx包,会调用Precedence.dtsx,Script Task 2选择是,Script Task 4选择否。下一个消息对话框是Parent包的OnError事件处理程序,为什么?因为Precedence.dtsx包的OnError事件处理程序禁用了。
再次,你可能希望使用子包事件处理程序隔离部分事件并停止冒泡。对于这样的设计有可用的案例。
总结
这一篇,我们关注在事件行为。我们演示了两种修改事件冒泡默认行为的方法。使用DisableEventHandlers属性禁用事件处理程序,使用事件处理程序的脚本任务操纵System::Propagate变量的值。我们介绍了父子模式并检测了父子模式中的事件行为。