Поделиться через


Добавление поддержки отладки в пользовательскую задачу

Обработчик среды выполнения служб Integration Services дает возможность приостанавливать пакеты, задачи и другие типы контейнеров во время выполнения при помощи точек останова. Использование точек останова позволяет просматривать и исправлять ошибки, мешающие правильной работе приложения или задач. Архитектура точек останова позволяет оценивать во время выполнения значения объектов в пакете в определенных точках выполнения при приостановке обработки задачи.

Разработчики пользовательских задач могут использовать эту архитектуру для создания целевых объектов пользовательских точек останова с помощью интерфейса IDTSBreakpointSite и его родительского интерфейса IDTSSuspend. Интерфейс IDTSBreakpointSite определяет взаимодействие между обработчиком среды выполнения и задачей для создания узлов или целевых объектов пользовательских точек останова и управления этими узлами и объектами. Интерфейс IDTSSuspend предоставляет методы и свойства, вызываемые обработчиком среды выполнения для уведомления задачи о приостановке или возобновлении ее обработки.

Узлом или целевым объектом точки останова является точка в выполнении задачи, где обработка может быть приостановлена. Пользователи выбирают из доступных узлов точек останова с помощью диалогового окна Задание точек останова. Например, дополнительно к параметрам точек останова по умолчанию контейнер «цикл по каждому элементу» предлагает параметр «Прервать в начале каждого повторения цикла».

Когда во время выполнения задача достигает целевого объекта точки останова, она оценивает целевой объект точки останова для определения, включена ли точка останова. Включение указывает на то, что пользователь хочет остановить выполнение в этой точке останова. Если точка останова включена, задача инициирует событие OnBreakpointHit для обработчика среды выполнения. Обработчик среды выполнения реагирует на событие вызовом метода Suspend каждой задачи, которая выполняется в настоящее время в пакете. Выполнение задачи возобновляется, когда среда выполнения вызывает метод ResumeExecution приостановленной задачи.

Задачи, не использующие точек останова, должны тем не менее реализовывать интерфейсы IDTSBreakpointSite и IDTSSuspend. Это гарантирует, что задача правильно приостановится, если другие объекты в пакете инициируют события OnBreakpointHit.

Интерфейс IDTSBreakpointSite и класс BreakpointManager

Задачи создают целевые объекты точек останова, вызывая метод CreateBreakpointTarget класса BreakpointManager, предоставляя целочисленный идентификатор и строковое описание как параметры. Когда задача достигает точки в коде, которая содержит целевой объект точки останова, она оценивает целевой объект при помощи метода 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 приостановленных задач.

Приостановка и возобновление выполнения задачи подразумевают приостановку и возобновление потока выполнения задачи. В управляемом коде это выполняется с помощью класса ManualResetEvent в пространстве имен System.Threading платформы .NET Framework.

В следующем образце кода демонстрируется приостановка и возобновление выполнения задачи. Обратите внимание, что метод 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 (маленький)Будьте в курсе новых возможностей cлужб Integration Services

Чтобы загружать новейшую документацию, статьи, образцы и видеоматериалы от корпорации Майкрософт, а также лучшие решения от участников сообщества, посетите страницу служб Integration Services на сайтах MSDN или TechNet:

Чтобы получать автоматические уведомления об этих обновлениях, подпишитесь на RSS-каналы, предлагаемые на этой странице.

См. также

Задания