Přidání podpory pro ladění ve vlastní úloze

Platí pro:SQL Server SSIS Integration Runtime v rámci Azure Data Factory

Run-time engine Integration Services umožňuje pozastavení balíčků, úloh a dalších typů kontejnerů během provádění pomocí breakpointů. Použití breakpointů vám umožní přezkoumat a opravit chyby, které brání správnému běhu vaší aplikace nebo úloh. Architektura breakpointu umožňuje klientovi vyhodnotit hodnotu objektů v balíčku za běhu v definovaných bodech vykonání, zatímco je zpracování úloh pozastaveno.

Vývojáři vlastních úloh mohou tuto architekturu využít k vytváření vlastních cílů breakpointů pomocí IDTSBreakpointSite rozhraní a jeho nadřazeného interface, IDTSSuspend. Rozhraní IDTSBreakpointSite definuje interakci mezi run-time enginem a úkolem vytváření a správy vlastních breakpointových míst nebo cílů. Rozhraní IDTSSuspend poskytuje metody a vlastnosti, které jsou volány runtime enginem, aby upozornily úkol k pozastavení nebo obnovení jeho vykonání.

Místo nebo cíl s přerušujícím bodem je bod v průběhu úkolu, kdy lze zpracování pozastavit. Uživatelé si vybírají z dostupných míst pro přerušení v dialogovém okně Set Breakpoint . Například kromě výchozích možností přerušení nabízí Foreach Loop Container možnost "Přerušit na začátku každé iterace smyčky".

Když úkol během provádění dosáhne cíle přerušení, vyhodnotí tento cíl, aby zjistil, zda je bod přerušení povolen. To naznačuje, že uživatel chce, aby se vykonání zastavilo právě v tomto bodě přerušení. Pokud je breakpoint povolen, úkol tuto událost přenese OnBreakpointHit do run-time enginu. Run-time engine reaguje na událost vyvoláním metody Suspend každé úlohy, která právě běží v balíčku. Vykonání úkolu pokračuje, když runtime vyvolá metodu ResumeExecution pozastavené úlohy.

Úlohy, které nepoužívají breakpointy, by měly stále implementovat IDTSBreakpointSite rozhraní and IDTSSuspend (rozhraní). To zajišťuje, že úkol je správně pozastaven, když jiné objekty v balíčku vyvolávají OnBreakpointHit události.

Rozhraní IDTSBreakpointSite a BreakpointManager

Úlohy vytvářejí cíle breakpointů voláním CreateBreakpointTarget metody , BreakpointManagerkterá poskytuje celočíselné ID a popis řetězce jako parametry. Když úkol dosáhne bodu v kódu, který obsahuje cíl pro bod přerušení, vyhodnotí cíl přerušení pomocí IsBreakpointTargetEnabled metody k určení, zda je tento bod povolen. Pokud je to pravda, úkol informuje run-time engine zvýšením události OnBreakpointHit .

Rozhraní IDTSBreakpointSite definuje jednu metodu, AcceptBreakpointManager, která je vyvolána run-time enginem při vytváření úkolů. Tato metoda poskytuje jako parametr BreakpointManager objekt, který pak úkol používá k vytvoření a správě svých breakpointů. Úlohy by měly uložit lokálně BreakpointManager pro použití během metod Validate a Execute ( Valide) a Execute (Valide) a Execute (Vykonání ).

Následující ukázkový kód ukazuje, jak vytvořit cíl pro bod zlomu pomocí .BreakpointManager Vzorek volá metodu OnBreakpointHit pro zvýšení události.

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  

Rozhraní IDTSSuspend

Rozhraní IDTSSuspend definuje metody, které jsou volány run-time enginem, když pozastaví nebo obnoví provádění úkolu. Rozhraní IDTSSuspend je implementováno IDTSBreakpointSite rozhraním a jeho metody Suspend a ResumeExecute jsou obvykle přepsány vlastním úkolem. Když run-time engine obdrží událost OnBreakpointHit od úlohy, volá metodu Suspend každé běžící úlohy a upozorní úkoly k pozastavení. Když klient obnoví vykonání, run-time engine vyvolá metodu ResumeExecution u pozastavených úloh.

Pozastavení a obnovení vykonávání úkolu zahrnuje pozastavení a obnovení vlákna vykonání úkolu. Ve spravovaném kódu to děláte pomocí třídy ManualResetEvent v jmenném prostoru System.Threading v rámci .NET Frameworku.

Následující ukázka kódu ukazuje pozastavení a obnovení plnění úkolu. Všimněte si, že metoda Execute se změnila oproti předchozímu vzorku kódu a vlákno vykonání je pozastaveno při spuštění breakpointu.

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