Utilisation de sorties d'erreur dans un composant de flux de données

S’applique à :SQL Server SSIS Integration Runtime dans Azure Data Factory

Il est possible d'ajouter des objets IDTSOutput100 spéciaux appelés sorties d'erreur à des composants afin de permettre à un composant de rediriger les lignes qu'il ne parvient pas à traiter pendant l'exécution. Les problèmes qu'un composant peut rencontrer sont en général classés en tant qu'erreurs ou troncations et sont propres à chaque composant. Les composants qui fournissent des sorties d'erreur offrent à leurs utilisateurs la flexibilité de gérer les conditions d'erreur en filtrant les lignes d'erreur du jeu de résultats, en provoquant l'échec du composant lorsqu'un problème se produit ou en ignorant des erreurs afin de continuer.

Pour implémenter et prendre en charge les sorties d’erreur dans un composant, vous devez commencer par définir la propriété UsesDispositions du composant sur true. Ensuite, vous devez ajouter une sortie au composant dont la propriété IsErrorOut a la valeur true. Enfin, le composant doit contenir un code qui redirige les lignes vers la sortie d'erreur lorsque des erreurs ou troncations se produisent. Cette rubrique couvre ces trois étapes et explique les différences entre les sorties d'erreur synchrones et asynchrones.

Création d'une sortie d'erreur

Vous créez une sortie d’erreur en appelant la méthode New de la propriété OutputCollection, puis en définissant la propriété IsErrorOut de la nouvelle sortie sur true. Si la sortie est asynchrone, vous n'avez rien d'autre à lui faire. Si la sortie est synchrone, et qu'il existe une autre sortie synchrone avec la même entrée, vous devez également définir les propriétés ExclusionGroup et SynchronousInputID. Ces deux propriétés doivent avoir les mêmes valeurs que l'autre sortie synchrone avec la même entrée. Si ces propriétés ne sont pas définies sur une valeur différente de zéro, les lignes fournies par l'entrée sont envoyées aux deux sorties synchrones avec l'entrée.

Lorsqu'un composant rencontre une erreur ou troncation pendant l'exécution, il continue selon les paramètres des propriétés ErrorRowDisposition et TruncationRowDisposition de l'entrée ou la sortie, ou de l'entrée ou la colonne de sortie, dans laquelle l'erreur s'est produite. La valeur de ces propriétés doit être définie par défaut sur RD_NotUsed. Lorsque la sortie d'erreur du composant est connectée à un composant en aval, cette propriété est définie par l'utilisateur du composant et permet à l'utilisateur de contrôler la manière dont le composant gère l'erreur ou la troncation.

Remplissage de colonnes d'erreur

Lorsqu'une sortie d'erreur est créée, la tâche de flux de données ajoute automatiquement deux colonnes à la collection de colonnes de sortie. Ces colonnes sont utilisées par les composants pour spécifier l'ID de la colonne qui a provoqué l'erreur ou la troncation, puis pour fournir le code d'erreur spécifique au composant. Ces colonnes sont générées automatiquement, mais les valeurs qu'elles contiennent doivent être définies par le composant.

La méthode utilisée pour définir les valeurs de ces colonnes varie selon que la sortie d'erreur est synchrone ou asynchrone. Les composants avec des sorties synchrones appellent la méthode DirectErrorRow, décrite de manière plus approfondie dans la section suivante, puis fournissent le code d'erreur et les valeurs de colonnes d'erreur en tant que paramètres. Les composants avec des sorties asynchrones ont deux possibilités pour définir les valeurs de ces colonnes. Ils peuvent soit appeler la méthode SetErrorInfo du tampon de sortie et fournir les valeurs, soit localiser les colonnes d'erreur dans le tampon à l'aide de la méthode FindColumnByLineageID et définir directement les valeurs des colonnes. Toutefois, puisque les noms des colonnes ou leur emplacement dans la collection de colonnes de sortie peuvent avoir été modifiés, la seconde méthode risque de ne pas être fiable. La méthode SetErrorInfo définit automatiquement les valeurs dans ces colonnes d'erreur sans avoir à les localiser manuellement.

Si vous devez obtenir la description d'erreur qui correspond à un code d'erreur spécifique, vous pouvez utiliser la méthode GetErrorDescription de l'interface IDTSComponentMetaData100, disponible via la propriété ComponentMetaData du composant.

Les exemples de code suivants présentent un composant qui possède une entrée et deux sorties, dont une sortie d'erreur. Le premier exemple indique comment créer une sortie d'erreur synchrone avec l'entrée. Le second exemple indique comment créer une sortie d'erreur asynchrone.

public override void ProvideComponentProperties()  
{  
    // Specify that the component has an error output.  
    ComponentMetaData.UsesDispositions = true;  
    // Create the input.  
    IDTSInput100 input = ComponentMetaData.InputCollection.New();  
    input.Name = "Input";  
    input.ErrorRowDisposition = DTSRowDisposition.RD_NotUsed;  
    input.ErrorOrTruncationOperation = "A string describing the possible error or truncation that may occur during execution.";  
  
    // Create the default output.  
    IDTSOutput100 output = ComponentMetaData.OutputCollection.New();  
    output.Name = "Output";  
    output.SynchronousInputID = input.ID;  
    output.ExclusionGroup = 1;  
  
    // Create the error output.  
    IDTSOutput100 errorOutput = ComponentMetaData.OutputCollection.New();  
    errorOutput.IsErrorOut = true;  
    errorOutput.Name = "ErrorOutput";  
    errorOutput.SynchronousInputID = input.ID;  
    errorOutput.ExclusionGroup = 1;  
  
}  
Public  Overrides Sub ProvideComponentProperties()   
  
 ' Specify that the component has an error output.  
 ComponentMetaData.UsesDispositions = True   
  
 Dim input As IDTSInput100 = ComponentMetaData.InputCollection.New   
  
 ' Create the input.  
 input.Name = "Input"   
 input.ErrorRowDisposition = DTSRowDisposition.RD_NotUsed   
 input.ErrorOrTruncationOperation = "A string describing the possible error or truncation that may occur during execution."   
  
 ' Create the default output.  
 Dim output As IDTSOutput100 = ComponentMetaData.OutputCollection.New   
 output.Name = "Output"   
 output.SynchronousInputID = input.ID   
 output.ExclusionGroup = 1   
  
 ' Create the error output.  
 Dim errorOutput As IDTSOutput100 = ComponentMetaData.OutputCollection.New   
 errorOutput.IsErrorOut = True   
 errorOutput.Name = "ErrorOutput"   
 errorOutput.SynchronousInputID = input.ID   
 errorOutput.ExclusionGroup = 1   
  
End Sub  

L'exemple de code suivant crée une sortie d'erreur asynchrone.

public override void ProvideComponentProperties()  
{  
    // Specify that the component has an error output.  
    ComponentMetaData.UsesDispositions = true;  
  
    // Create the input.  
    IDTSInput100 input = ComponentMetaData.InputCollection.New();  
    input.Name = "Input";  
    input.ErrorRowDisposition = DTSRowDisposition.RD_NotUsed;  
    input.ErrorOrTruncationOperation = "A string describing the possible error or truncation that may occur during execution.";  
  
    // Create the default output.  
    IDTSOutput100 output = ComponentMetaData.OutputCollection.New();  
    output.Name = "Output";  
  
    // Create the error output.  
    IDTSOutput100 errorOutput = ComponentMetaData.OutputCollection.New();  
    errorOutput.Name = "ErrorOutput";  
    errorOutput.IsErrorOut = true;  
}  
Public  Overrides Sub ProvideComponentProperties()   
  
 ' Specify that the component has an error output.  
 ComponentMetaData.UsesDispositions = True   
  
 ' Create the input.  
 Dim input As IDTSInput100 = ComponentMetaData.InputCollection.New   
  
 ' Create the default output.  
 input.Name = "Input"   
 input.ErrorRowDisposition = DTSRowDisposition.RD_NotUsed   
 input.ErrorOrTruncationOperation = "A string describing the possible error or truncation that may occur during execution."   
  
 ' Create the error output.  
 Dim output As IDTSOutput100 = ComponentMetaData.OutputCollection.New   
 output.Name = "Output"   
 Dim errorOutput As IDTSOutput100 = ComponentMetaData.OutputCollection.New   
 errorOutput.Name = "ErrorOutput"   
 errorOutput.IsErrorOut = True   
  
End Sub  

Redirection d'une ligne vers une sortie d'erreur

Après avoir ajouté une sortie d'erreur à un composant, vous devez fournir un code qui gère les conditions d'erreur ou de troncation propres au composant et qui redirige les lignes d'erreur ou de troncation vers la sortie d'erreur. Vous pouvez procéder de deux manières pour cela, selon que la sortie d'erreur est synchrone ou asynchrone.

Redirection d'une ligne avec des sorties synchrones

Les lignes sont envoyées aux sorties synchrones en appelant la méthode DirectErrorRow de la classe PipelineBuffer. L'appel de méthode inclut en tant que paramètres l'ID de la sortie d'erreur, le code d'erreur défini par le composant et l'index de la colonne que le composant n'a pas pu traiter.

L'exemple de code suivant montre comment diriger une ligne dans un tampon vers une sortie d'erreur synchrone à l'aide de la méthode DirectErrorRow.

public override void ProcessInput(int inputID, PipelineBuffer buffer)  
{  
        IDTSInput100 input = ComponentMetaData.InputCollection.GetObjectByID(inputID);  
  
        // This code sample assumes the component has two outputs, one the default,  
        // the other the error output. If the errorOutputIndex returned from GetErrorOutputInfo  
        // is 0, then the default output is the second output in the collection.  
        int defaultOutputID = -1;  
        int errorOutputID = -1;  
        int errorOutputIndex = -1;  
  
        GetErrorOutputInfo(ref errorOutputID,ref errorOutputIndex);  
  
        if (errorOutputIndex == 0)  
            defaultOutputID = ComponentMetaData.OutputCollection[1].ID;  
        else  
            defaultOutputID = ComponentMetaData.OutputCollection[0].ID;  
  
        while (buffer.NextRow())  
        {  
            try  
            {  
                // TODO: Implement code to process the columns in the buffer row.  
  
                // Ideally, your code should detect potential exceptions before they occur, rather  
                // than having a generic try/catch block such as this.   
                // However, because the error or truncation implementation is specific to each component,  
                // this sample focuses on actually directing the row, and not a single error or truncation.  
  
                // Unless an exception occurs, direct the row to the default   
                buffer.DirectRow(defaultOutputID);  
            }  
            catch  
            {  
                // Yes, has the user specified to redirect the row?  
                if (input.ErrorRowDisposition == DTSRowDisposition.RD_RedirectRow)  
                {  
                    // Yes, direct the row to the error output.  
                    // TODO: Add code to include the errorColumnIndex.  
                    buffer.DirectErrorRow(errorOutputID, 0, errorColumnIndex);  
                }  
                else if (input.ErrorRowDisposition == DTSRowDisposition.RD_FailComponent || input.ErrorRowDisposition == DTSRowDisposition.RD_NotUsed)  
                {  
                    // No, the user specified to fail the component, or the error row disposition was not set.  
                    throw new Exception("An error occurred, and the DTSRowDisposition is either not set, or is set to fail component.");  
                }  
                else  
                {  
                    // No, the user specified to ignore the failure so   
                    // direct the row to the default output.  
                    buffer.DirectRow(defaultOutputID);  
                }  
  
            }  
        }  
}  
Public  Overrides Sub ProcessInput(ByVal inputID As Integer, ByVal buffer As PipelineBuffer)   
   Dim input As IDTSInput100 = ComponentMetaData.InputCollection.GetObjectByID(inputID)   
  
   ' This code sample assumes the component has two outputs, one the default,  
   ' the other the error output. If the errorOutputIndex returned from GetErrorOutputInfo  
   ' is 0, then the default output is the second output in the collection.  
   Dim defaultOutputID As Integer = -1   
   Dim errorOutputID As Integer = -1   
   Dim errorOutputIndex As Integer = -1   
  
   GetErrorOutputInfo(errorOutputID, errorOutputIndex)   
  
   If errorOutputIndex = 0 Then   
     defaultOutputID = ComponentMetaData.OutputCollection(1).ID   
   Else   
     defaultOutputID = ComponentMetaData.OutputCollection(0).ID   
   End If   
  
   While buffer.NextRow   
     Try   
       ' TODO: Implement code to process the columns in the buffer row.  
  
       ' Ideally, your code should detect potential exceptions before they occur, rather  
       ' than having a generic try/catch block such as this.   
       ' However, because the error or truncation implementation is specific to each component,  
       ' this sample focuses on actually directing the row, and not a single error or truncation.  
  
       ' Unless an exception occurs, direct the row to the default   
       buffer.DirectRow(defaultOutputID)   
     Catch   
       ' Yes, has the user specified to redirect the row?  
       If input.ErrorRowDisposition = DTSRowDisposition.RD_RedirectRow Then   
         ' Yes, direct the row to the error output.  
         ' TODO: Add code to include the errorColumnIndex.  
         buffer.DirectErrorRow(errorOutputID, 0, errorColumnIndex)   
       Else   
         If input.ErrorRowDisposition = DTSRowDisposition.RD_FailComponent OrElse input.ErrorRowDisposition = DTSRowDisposition.RD_NotUsed Then   
           ' No, the user specified to fail the component, or the error row disposition was not set.  
           Throw New Exception("An error occurred, and the DTSRowDisposition is either not set, or is set to fail component.")   
         Else   
           ' No, the user specified to ignore the failure so   
           ' direct the row to the default output.  
           buffer.DirectRow(defaultOutputID)   
         End If   
       End If   
     End Try   
   End While   
End Sub  

Redirection d'une ligne avec des sorties asynchrones

Au lieu de diriger des lignes vers une sortie, comme cela est fait avec des sorties d'erreur synchrones, les composants avec des sorties asynchrones envoient une ligne vers une sortie d'erreur en ajoutant explicitement une ligne à la sortie PipelineBuffer. L'implémentation d'un composant qui utilise des sorties d'erreur asynchrones implique d'ajouter à la sortie d'erreur des colonnes fournies aux composants en aval et de mettre en cache le tampon de sortie pour la sortie d'erreur fournie au composant pendant la méthode PrimeOutput. L’implémentation d’un composant à sorties asynchrones est décrite en détail dans la rubrique Développement d’un composant de transformation personnalisé à sorties asynchrones. Si les colonnes ne sont pas ajoutées explicitement à la sortie d'erreur, la ligne du tampon ajoutée au tampon de sortie contient uniquement les deux colonnes d'erreur.

Pour envoyer une ligne vers une sortie d'erreur asynchrone, vous devez ajouter une ligne au tampon de sortie d'erreur. Parfois, une ligne peut avoir déjà été ajoutée au tampon de sortie sans erreur et vous devez la supprimer en utilisant la méthode RemoveRow. Ensuite, vous définissez les valeurs des colonnes du tampon de sortie, et enfin, vous appelez la méthode SetErrorInfo pour fournir le code d'erreur propre au composant et la valeur de la colonne d'erreur.

L'exemple suivant montre comment utiliser une sortie d'erreur pour un composant avec des sorties asynchrones. Lorsque l'erreur simulée se produit, le composant ajoute une ligne au tampon de sortie d'erreur, copie les valeurs ajoutées précédemment au tampon de sortie sans erreur vers le tampon de sortie d'erreur, supprime la ligne ajoutée au tampon de sortie sans erreur et, enfin, définit le code d'erreur et les valeurs des colonnes d'erreur en appelant la méthode SetErrorInfo.

int []columnIndex;  
int errorOutputID = -1;  
int errorOutputIndex = -1;  
  
public override void PreExecute()  
{  
    IDTSOutput100 defaultOutput = null;  
  
    this.GetErrorOutputInfo(ref errorOutputID, ref errorOutputIndex);  
    foreach (IDTSOutput100 output in ComponentMetaData.OutputCollection)  
    {  
        if (output.ID != errorOutputID)  
            defaultOutput = output;  
    }  
  
    columnIndex = new int[defaultOutput.OutputColumnCollection.Count];  
  
    for(int col =0 ; col < defaultOutput.OutputColumnCollection.Count; col++)  
    {  
        IDTSOutputColumn100 column = defaultOutput.OutputColumnCollection[col];  
        columnIndex[col] = BufferManager.FindColumnByLineageID(defaultOutput.Buffer, column.LineageID);  
    }  
}  
  
public override void PrimeOutput(int outputs, int[] outputIDs, PipelineBuffer[] buffers)  
{  
    for( int x=0; x < outputs; x++ )  
    {  
        if (outputIDs[x] == errorOutputID)  
            this.errorBuffer = buffers[x];  
        else  
            this.defaultBuffer = buffers[x];  
    }  
  
    int rows = 100;  
  
    Random random = new Random(System.DateTime.Now.Millisecond);  
  
    for (int row = 0; row < rows; row++)  
    {  
        try  
        {  
            defaultBuffer.AddRow();  
  
            for (int x = 0; x < columnIndex.Length; x++)  
                defaultBuffer[columnIndex[x]] = random.Next();  
  
            // Simulate an error.  
            if ((row % 2) == 0)  
                throw new Exception("A simulated error.");  
        }  
        catch  
        {  
            // Add a row to the error buffer.  
            errorBuffer.AddRow();  
  
            // Get the values from the default buffer  
            // and copy them to the error buffer.  
            for (int x = 0; x < columnIndex.Length; x++)  
                errorBuffer[columnIndex[x]] = defaultBuffer[columnIndex[x]];  
  
            // Set the error information.  
            errorBuffer.SetErrorInfo(errorOutputID, 1, 0);  
  
            // Remove the row that was added to the default buffer.  
            defaultBuffer.RemoveRow();  
        }  
    }  
  
    if (defaultBuffer != null)  
        defaultBuffer.SetEndOfRowset();  
  
    if (errorBuffer != null)  
        errorBuffer.SetEndOfRowset();  
}  
Private columnIndex As Integer()   
Private errorOutputID As Integer = -1   
Private errorOutputIndex As Integer = -1   
  
Public  Overrides Sub PreExecute()   
 Dim defaultOutput As IDTSOutput100 = Nothing   
 Me.GetErrorOutputInfo(errorOutputID, errorOutputIndex)   
 For Each output As IDTSOutput100 In ComponentMetaData.OutputCollection   
   If Not (output.ID = errorOutputID) Then   
     defaultOutput = output   
   End If   
 Next   
 columnIndex = New Integer(defaultOutput.OutputColumnCollection.Count) {}   
 Dim col As Integer = 0   
 While col < defaultOutput.OutputColumnCollection.Count   
   Dim column As IDTSOutputColumn100 = defaultOutput.OutputColumnCollection(col)   
   columnIndex(col) = BufferManager.FindColumnByLineageID(defaultOutput.Buffer, column.LineageID)   
   System.Math.Min(System.Threading.Interlocked.Increment(col),col-1)   
 End While   
End Sub   
  
Public  Overrides Sub PrimeOutput(ByVal outputs As Integer, ByVal outputIDs As Integer(), ByVal buffers As PipelineBuffer())   
 Dim x As Integer = 0   
 While x < outputs   
   If outputIDs(x) = errorOutputID Then   
     Me.errorBuffer = buffers(x)   
   Else   
     Me.defaultBuffer = buffers(x)   
   End If   
   System.Math.Min(System.Threading.Interlocked.Increment(x),x-1)   
 End While   
 Dim rows As Integer = 100   
 Dim random As Random = New Random(System.DateTime.Now.Millisecond)   
 Dim row As Integer = 0   
 While row < rows   
   Try   
     defaultBuffer.AddRow   
     Dim x As Integer = 0   
     While x < columnIndex.Length   
       defaultBuffer(columnIndex(x)) = random.Next   
       System.Math.Min(System.Threading.Interlocked.Increment(x),x-1)   
     End While   
     ' Simulate an error.  
     If (row Mod 2) = 0 Then   
       Throw New Exception("A simulated error.")   
     End If   
   Catch   
     ' Add a row to the error buffer.  
     errorBuffer.AddRow   
     ' Get the values from the default buffer  
     ' and copy them to the error buffer.  
     Dim x As Integer = 0   
     While x < columnIndex.Length   
       errorBuffer(columnIndex(x)) = defaultBuffer(columnIndex(x))   
       System.Math.Min(System.Threading.Interlocked.Increment(x),x-1)   
     End While   
     ' Set the error information.  
     errorBuffer.SetErrorInfo(errorOutputID, 1, 0)   
     ' Remove the row that was added to the default buffer.  
     defaultBuffer.RemoveRow   
   End Try   
   System.Math.Min(System.Threading.Interlocked.Increment(row),row-1)   
 End While   
 If Not (defaultBuffer Is Nothing) Then   
   defaultBuffer.SetEndOfRowset   
 End If   
 If Not (errorBuffer Is Nothing) Then   
   errorBuffer.SetEndOfRowset   
 End If   
End Sub  

Voir aussi

Gestion des erreurs dans les données
Utilisation de sorties d’erreur