共用方式為


新增自訂工作中的偵錯支援

Integration Services 執行階段引擎允許在執行期間,使用中斷點暫停封裝、工作和其他類型的容器。使用中斷點可讓您檢閱和修正妨礙應用程式或工作正確執行的錯誤。中斷點架構可讓用戶端在定義的執行點暫停工作處理,以評估封裝中物件的執行階段值。

自訂工作開發人員可以使用 IDTSBreakpointSite 介面及其父介面 IDTSSuspend,以利用此架構建立自訂中斷點目標。IDTSBreakpointSite 介面會定義執行階段引擎與工作之間的互動,以建立和管理自訂中斷點位置或目標。IDTSSuspend 介面提供執行階段引擎呼叫的方法與屬性,通知工作暫停或是繼續其執行。

中斷點位置或目標是在工作執行中可以暫停處理的點。使用者可以從 [設定中斷點] 對話方塊中的可用中斷點位置選取。例如,除了預設中斷點選項之外,[Foreach 迴圈容器] 提供 [在迴圈的每一次反覆運算開始時中斷] 選項。

當工作在執行期間到達中斷點目標時,它會評估中斷點目標以決定是否啟用中斷點。這指出使用者希望在該中斷點停止執行。如果啟用中斷點,工作會將 OnBreakpointHit 事件引發至執行階段引擎。執行階段引擎會呼叫目前在封裝中執行的每項工作之 Suspend 方法以回應事件。當執行階段呼叫已暫停工作的 ResumeExecution 方法時,工作執行會繼續。

不使用中斷點的工作仍應實作 IDTSBreakpointSiteIDTSSuspend 介面。這可確保當封裝中的其他物件引發 OnBreakpointHit 事件時,正確地暫停工作。

IDTSBreakpointSite 介面與 BreakpointManager

工作透過呼叫 BreakpointManagerCreateBreakpointTarget 方法,並提供整數識別碼和字串描述做為參數,以建立中斷點目標。當工作到達其程式碼中包含中斷點目標的點時,它會使用 IsBreakpointTargetEnabled 方法評估中斷點目標,以判斷是否已啟用該中斷點。如果是 true,工作會引發 OnBreakpointHit 事件,以通知執行階段引擎。

IDTSBreakpointSite 介面會定義單一方法 AcceptBreakpointManager,後者會由執行階段引擎於工作建立期間呼叫。這個方法會以參數形式提供 BreakpointManager 物件,然後由工作使用該物件來建立並管理其中斷點。工作會在本機上儲存 BreakpointManager,以供在 Validate 與 Execute 方法期間使用。

下列範例程式碼示範如何使用 BreakpointManager 來建立中斷點目標。範例會呼叫 OnBreakpointHit 方法以引發事件。

public void AcceptBreakpointManager( BreakpointManager breakPointManager )
{
   //   Store the breakpoint manager locally.
   this.bpm  = breakPointManager;
}
public override DTSExecResult Execute( Connections connections,
  Variables variables, IDTSComponentEvents events,
  IDTSLogging log, DtsTransaction txn)
{
   //   Create a breakpoint.
   this.bpm.CreateBreakPointTarget( 1 , "A sample breakpoint target." );
...
   if( this.bpm.IsBreakpointTargetEnabled( 1 ) == true )
      events.OnBreakpointHit( this.bpm.GetBreakpointTarget( 1 ) );
}
Public Sub AcceptBreakpointManager(ByVal breakPointManager As BreakpointManager)

   ' Store the breakpoint manager locally.
   Me.bpm  = breakPointManager

End Sub

Public Overrides Function Execute(ByVal connections As Connections, _
  ByVal variables As Variables, ByVal events As IDTSComponentEvents, _
  ByVal log As IDTSLogging, ByVal txn As DtsTransaction) As DTSExecResult

   ' Create a breakpoint.
   Me.bpm.CreateBreakPointTarget(1 , "A sample breakpoint target.")

   If Me.bpm.IsBreakpointTargetEnabled(1) = True Then
      events.OnBreakpointHit(Me.bpm.GetBreakpointTarget(1))
   End If

End Function

IDTSSuspend 介面

IDTSSuspend 介面會定義當執行階段引擎暫停或是繼續工作的執行時,該引擎呼叫的方法。IDTSSuspend 介面是由 IDTSBreakpointSite 介面實作,而且自訂工作通常會覆寫其 Suspend 與 ResumeExecution 方法。當執行階段引擎從工作收到 OnBreakpointHit 事件時,它會呼叫每個執行中工作的 Suspend 方法,以通知工作暫停。當用戶端繼續執行時,執行階段引擎會呼叫已暫停工作的 ResumeExecution 方法。

暫停和繼續工作執行需要暫停和繼續工作的執行緒。在 Managed 程式碼中,您使用 .NET Framework 之 System.Threading 命名空間中的 ManualResetEvent 類別,來執行這項動作。

下列程式碼範例示範工作執行的暫停與繼續。請注意,Execute 方法和上一個程式碼範例不同,而且會在引發中斷點時暫停執行緒。

private ManualResetEvent m_suspended = new ManualResetEvent( true );
private ManualResetEvent m_canExecute = new ManualResetEvent( true );
private int   m_suspendRequired = 0;
private int   m_debugMode = 0;

public override DTSExecResult Execute( Connections connections, Variables variables, IDTSComponentEvents events, IDTSLogging log, DtsTransaction txn)
{
   // While a task is not executing, it is suspended.  
   // Now that we are executing,
   // change to not suspended.
   ChangeEvent(m_suspended, false);
   
   // Check for a suspend before doing any work, 
   // in case the suspend and execute calls
   // were initiated at virtually the same time.
   CheckAndSuspend();
   CheckAndFireBreakpoint( componentEvents, 1);
}
private void CheckAndSuspend()
{
   // Loop until we can execute.  
   // The loop is required rather than a simple If
   // because there is a time between the return from WaitOne and the
   // reset that we might receive another Suspend call.  
   // Suspend() will see that we are suspended
   // and return.  So we need to rewait.
   while (!m_canExecute.WaitOne(0, false))
   {
      ChangeEvent(m_suspended, true);
      m_canExecute.WaitOne();
      ChangeEvent(m_suspended, false);
   }
}
private void CheckAndFireBreakpoint(IDTSComponentEvents events, int breakpointID)
{
   // If the breakpoint is enabled, fire it.
   if (m_debugMode != 0 &&    this.bpm.IsBreakpointTargetEnabled(breakpointID))
   {
      //   Enter a suspend mode before firing the breakpoint.  
      //   Firing the breakpoint will cause the runtime 
      //   to call Suspend on this task.  
      //   Because we are blocked on the breakpoint, 
      //   we are suspended.
      ChangeEvent(m_suspended, true);
      events.OnBreakpointHit(this.bpm.GetBreakpointTarget(breakpointID));
      ChangeEvent(m_suspended, false);
   }
   // Check for a suspension for two reasons: 
   //   1. If we are at a point where we could fire a breakpoint, 
   //      we are at a valid suspend point.  Even if we didn't hit a
   //      breakpoint, the runtime may have called suspend, 
   //      so check for it.     
   //   2. Between the return from OnBreakpointHit 
   //      and the reset of the event, it is possible to have
   //      received a suspend call from which we returned because 
   //      we were already suspended.  We need to be sure it is okay
   //      to continue executing now.
   CheckAndSuspend();
}
static void ChangeEvent(ManualResetEvent e, bool shouldSet)
{
   bool succeeded;
   if (shouldSet)
      succeeded = e.Set();
   else
      succeeded = e.Reset();

   if (!succeeded)
      throw new Exception("Synchronization object failed.");
            
}
public bool SuspendRequired
{
   get   {return m_suspendRequired != 0;}
   set
   {
      // This lock is also taken by Suspend().  
      // Because it is possible for the package to be
      // suspended and resumed in quick succession, 
      // this property might be set before
      // the actual Suspend() call.  
      // Without the lock, the Suspend() might reset the canExecute
      // event after we set it to abort the suspension.
      lock (this)
      {
         Interlocked.Exchange(ref m_suspendRequired, value ? 1 : 0);
         if (!value)
            ResumeExecution();
      }
   }
}
public void ResumeExecution()
{
   ChangeEvent( m_canExecute,true );
}
public void Suspend()
{
   // This lock is also taken by the set SuspendRequired method.  
   // It prevents this call from overriding an 
   // aborted suspension.  See comments in set SuspendRequired.
   lock (this)
   {
      // If a Suspend is required, do it.
      if (m_suspendRequired != 0)
         ChangeEvent(m_canExecute, false);
   }
   // We can't return from Suspend until the task is "suspended".
   // This can happen one of two ways: 
   // the m_suspended event occurs, indicating that the execute thread
   // has suspended, or the canExecute flag is set, 
   // indicating that a suspend is no longer required.
   WaitHandle [] suspendOperationComplete = {m_suspended, m_canExecute};
   WaitHandle.WaitAny(suspendOperationComplete);
}
Private m_suspended As ManualResetEvent = New ManualResetEvent(True)
Private m_canExecute As ManualResetEvent = New ManualResetEvent(True)
Private m_suspendRequired As Integer = 0
Private m_debugMode As Integer = 0

Public Overrides Function Execute(ByVal connections As Connections, _
ByVal variables As Variables, ByVal events As IDTSComponentEvents, _
ByVal log As IDTSLogging, ByVal txn As DtsTransaction) As DTSExecResult

   ' While a task is not executing it is suspended.  
   ' Now that we are executing,
   ' change to not suspended.
   ChangeEvent(m_suspended, False)

   ' Check for a suspend before doing any work, 
   ' in case the suspend and execute calls
   ' were initiated at virtually the same time.
   CheckAndSuspend()
   CheckAndFireBreakpoint(componentEvents, 1)

End Function

Private Sub CheckAndSuspend()

   ' Loop until we can execute.  
   ' The loop is required rather than a simple if
   ' because there is a time between the return from WaitOne and the
   ' reset that we might receive another Suspend call.  
   ' Suspend() will see that we are suspended
   ' and return.  So we need to rewait.
   Do While Not m_canExecute.WaitOne(0, False)
              ChangeEvent(m_suspended, True)
              m_canExecute.WaitOne()
              ChangeEvent(m_suspended, False)
   Loop

End Sub

Private Sub CheckAndFireBreakpoint(ByVal events As IDTSComponentEvents, _
ByVal breakpointID As Integer)

   ' If the breakpoint is enabled, fire it.
   If m_debugMode <> 0 AndAlso Me.bpm.IsBreakpointTargetEnabled(breakpointID) Then
              '   Enter a suspend mode before firing the breakpoint.  
              '   Firing the breakpoint will cause the runtime 
              '   to call Suspend on this task.  
              '   Because we are blocked on the breakpoint, 
              '   we are suspended.
              ChangeEvent(m_suspended, True)
              events.OnBreakpointHit(Me.bpm.GetBreakpointTarget(breakpointID))
              ChangeEvent(m_suspended, False)
   End If

   ' Check for a suspension for two reasons: 
   '   1. If we are at a point where we could fire a breakpoint, 
   '         we are at a valid suspend point.  Even if we didn't hit a
   '         breakpoint, the runtime may have called suspend, 
   '         so check for it.   
   '   2. Between the return from OnBreakpointHit 
   '         and the reset of the event, it is possible to have
   '         received a suspend call from which we returned because 
   '         we were already suspended.  We need to be sure it is okay
   '         to continue executing now.
   CheckAndSuspend()

End Sub

Shared Sub ChangeEvent(ByVal e As ManualResetEvent, ByVal shouldSet As Boolean)

   Dim succeeded As Boolean
   If shouldSet Then
              succeeded = e.Set()
   Else
              succeeded = e.Reset()
   End If

   If (Not succeeded) Then
              Throw New Exception("Synchronization object failed.")
   End If
 
End Sub

Public Property SuspendRequired() As Boolean
   Get
               Return m_suspendRequired <> 0
  End Get
  Set
    ' This lock is also taken by Suspend().  
     '   Because it is possible for the package to be
     '   suspended and resumed in quick succession, 
     '   this property might be set before
     '   the actual Suspend() call.  
     '   Without the lock, the Suspend() might reset the canExecute
     '   event after we set it to abort the suspension.
              SyncLock Me
                         Interlocked.Exchange(m_suspendRequired,IIf(Value, 1, 0))
                         If (Not Value) Then
                                    ResumeExecution()
                         End If
              End SyncLock
   End Set
End Property

Public Sub ResumeExecution()
   ChangeEvent(m_canExecute,True)
End Sub

Public Sub Suspend()

   ' This lock is also taken by the set SuspendRequired method.  
   ' It prevents this call from overriding an 
   ' aborted suspension.  See comments in set SuspendRequired.
   SyncLock Me
   ' If a Suspend is required, do it.
              If m_suspendRequired <> 0 Then
                         ChangeEvent(m_canExecute, False)
              End If
   End SyncLock
   ' We can't return from Suspend until the task is "suspended".
   ' This can happen one of two ways: 
   ' the m_suspended event occurs, indicating that the execute thread
   ' has suspended, or the canExecute flag is set, 
   ' indicating that a suspend is no longer required.
   Dim suspendOperationComplete As WaitHandle() = {m_suspended, m_canExecute}
   WaitHandle.WaitAny(suspendOperationComplete)

End Sub
Integration Services 圖示 (小) 掌握 Integration Services 的最新狀態

若要取得 Microsoft 的最新下載、文件、範例和影片以及社群中的選定解決方案,請瀏覽 MSDN 或 TechNet 上的 Integration Services 頁面:

若要得到這些更新的自動通知,請訂閱該頁面上所提供的 RSS 摘要。

請參閱

工作