{"id":347,"date":"2015-06-14T18:18:28","date_gmt":"2015-06-14T17:18:28","guid":{"rendered":"http:\/\/www.csopro.de\/biblog\/?p=347"},"modified":"2015-06-14T18:28:35","modified_gmt":"2015-06-14T17:28:35","slug":"quarantne-teil-3-anlegen-der-zustzlichen-komponenten","status":"publish","type":"post","link":"https:\/\/www.csopro.de\/biblog\/2015\/06\/quarantne-teil-3-anlegen-der-zustzlichen-komponenten\/","title":{"rendered":"Quarant&auml;ne-Teil 3: Anlegen der zus&auml;tzlichen Komponenten"},"content":{"rendered":"<p>Nachdem wir im letzten Blog-Kapitel alle Komponenten eines Data Flows durchlaufen konnten und dazu erkennen konnten, ob eine Fehlerbehandlung m\u00f6glich und noch nicht vorhanden ist, m\u00fcssen wir nun die Fehlerbehandlung in diesen F\u00e4llen einbauen.<\/p>\n<p>Die Fehlerbehandlung soll wie folgt funktionieren:<\/p>\n<ul>\n<li>Fehlerbehandlung auf Redirect Row einstellen<\/li>\n<li>Den Error Output in eine neu zu erstellende Derived Column leiten.<\/li>\n<li>In der Derived Column sollen einige zus\u00e4tzliche Attribute wie Fehlermeldung, aber auch Prim\u00e4rschl\u00fcssel hinzugef\u00fcgt werden<\/li>\n<li>Das Ergebnis l\u00e4uft dann in einen UNION ALL, der bereits im Paket existiert.<\/li>\n<\/ul>\n<p>Damit ist die Quarant\u00e4ne noch nicht ganz fertig. Das UNION ALL f\u00fchrt dann zu einem SQL-Server-Ziel. Dort werden die Spalten aus der Union All in die Datenbank geschrieben. Diesen Teil habe ich, da er nur einmal (je Data Flow) zu erstellen ist, aber manuell gel\u00f6st. Hier ist der Aufwand geringer als eine automatisierte L\u00f6sung zu implementieren.<\/p>\n<h1>Fehlerbehandlung auf Redirect Row umstellen<\/h1>\n<p>Interessanter Weise gibt es unterschiedliche Arten von Fehlerbehandlungen in SSIS. Bei manchen Komponenten wird die Fehlerbehandlung einzeln f\u00fcr jede Spalte (z.B. derived column) definiert, in anderen nur global f\u00fcr die gesamte Komponente, in manchen auch f\u00fcr beides.<\/p>\n<p>Der Lookup hat beide Fehlerbehandlungen:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image1.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image_thumb1.png\" alt=\"image\" width=\"644\" height=\"173\" border=\"0\" \/><\/a><\/p>\n<p>Hier ein Beispiel f\u00fcr die Fehlerbehandlung einer Derived Column:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image2.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image_thumb2.png\" alt=\"image\" width=\"581\" height=\"91\" border=\"0\" \/><\/a><\/p>\n<p>Im Advanced Editor sieht das dann so aus:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image3.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image_thumb3.png\" alt=\"image\" width=\"644\" height=\"239\" border=\"0\" \/><\/a><\/p>\n<p>Und die globale Komponenten-Fehlerbehandlung wird nicht verwendet:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image4.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image_thumb4.png\" alt=\"image\" width=\"644\" height=\"241\" border=\"0\" \/><\/a><\/p>\n<p>Wichtig ist zu bemerken, dass diese Einstellungen nat\u00fcrlich nicht in dem Error Output, sondern in dem normalen (bzw. allen normalen) Output(s) und Input(s) gemacht werden m\u00fcssen.<\/p>\n<p>Deswegen durchl\u00e4uft der Code alle Inputs und alle Spalten aller Inputs und das gleiche f\u00fcr die Outputs:<\/p>\n<blockquote><p>For Each outp As IDTSOutput100 In comp.OutputCollection<br \/>\n&#8218; globale Fehlerhandlung<br \/>\nIf outp.ErrorRowDisposition = DTSRowDisposition.RD_FailComponent Then<br \/>\noutp.ErrorRowDisposition = DTSRowDisposition.RD_RedirectRow<br \/>\nEnd If<br \/>\nIf outp.TruncationRowDisposition = DTSRowDisposition.RD_FailComponent Then<br \/>\noutp.TruncationRowDisposition = DTSRowDisposition.RD_RedirectRow<br \/>\nEnd If<br \/>\n&#8218;Fehlerbehandlung je Spalte<br \/>\nFor Each col As IDTSOutputColumn100 In outp.OutputColumnCollection<br \/>\nIf col.ErrorRowDisposition = DTSRowDisposition.RD_FailComponent Then<br \/>\ncol.ErrorRowDisposition = DTSRowDisposition.RD_RedirectRow<br \/>\nEnd If<br \/>\nIf col.TruncationRowDisposition = DTSRowDisposition.RD_FailComponent Then<br \/>\ncol.TruncationRowDisposition = DTSRowDisposition.RD_RedirectRow<br \/>\nEnd If<br \/>\nNext col<br \/>\nNext outp<\/p><\/blockquote>\n<p>Und das gleiche f\u00fcr die Inputs:<\/p>\n<blockquote><p>For Each inp As IDTSInput100 In comp.InputCollection<span style=\"color: #767676;\"> . . .<\/span><\/p><\/blockquote>\n<p>Am besten merkt man sich noch in einer boolschen Variablen, ob ein Redirect Row eingestellt wurde. Nur dann darf man n\u00e4mlich den n\u00e4chsten Schritt machen.<\/p>\n<p>Dies ist wichtig, da die Fehlermeldungen bei der SSIS-API-Entwicklung meist kryptisch sind, da .NET-Wrapper von der API verwendet werden. <span style=\"color: #767676;\">\u00a0<\/span><\/p>\n<h1>Eine Derived Column-Komponente erstellen<\/h1>\n<p>Wir erstellen sie so:<\/p>\n<blockquote><p>&#8217;neue Derived Column<br \/>\nDim compDerivedCol As IDTSComponentMetaData100 = pipe.ComponentMetaDataCollection.New()<br \/>\ncompDerivedCol.ComponentClassID = &#8222;DTSTransform.DerivedColumn&#8220;<br \/>\nDim DesignDerivedTransformColumns As CManagedComponentWrapper = compDerivedCol.Instantiate()<br \/>\nDesignDerivedTransformColumns.ProvideComponentProperties()<br \/>\ncompDerivedCol.Name = &#8222;Fehler_&#8220; &amp; comp.Name<br \/>\ncompDerivedCol.InputCollection(0).ExternalMetadataColumnCollection.IsUsed = False<br \/>\ncompDerivedCol.InputCollection(0).HasSideEffects = False<\/p><\/blockquote>\n<p>Man beachte das <em>Initiate()<\/em>. Dadurch wird die derived Column wie im Visual Studio auch instantiiert. Das hei\u00dft, man kann auf alle Voreinstellungen zugreifen.<\/p>\n<h1>Den Fehler-Output und die Derived Column verbinden<\/h1>\n<p>Hierzu legt man einen neuen Pfad an, der den Error Output (den wir im letzten Blog gefunden und gemerkt hatten) mit der Derived Column verbindet:<\/p>\n<blockquote><p>Dim path As IDTSPath100 = pipe.PathCollection.New()<br \/>\npath.AttachPathAndPropagateNotifications(comp.OutputCollection(nrOfErrorOutput), compDerivedCol.InputCollection(0))<\/p><\/blockquote>\n<h1>Zus\u00e4tzliche Spalten in der Derived Column anlegen<\/h1>\n<p>Da man das immer wieder braucht, habe ich dazu eine Methode erstellt.<\/p>\n<p>Diese erh\u00e4lt als Parameter:<\/p>\n<ul>\n<li>die Komponente (<em>compDerivedCol<\/em>)<\/li>\n<li>den Namen der Spalte<\/li>\n<li>den Datentyp, also zum Beispiel Microsoft.SqlServer.Dts.Runtime.Wrapper.DataType.DT_I4<br \/>\nHier kann man wunderbar den vorhandenen enum verwenden.<\/li>\n<li>die L\u00e4nge des Datentyps \u2013 nur bei Strings (o.\u00e4.) n\u00f6tig. Bei Integer kann man 0 angeben. Das System macht es automatisch richtig.<\/li>\n<li>Die Formel, also z.B. &#8222;(DT_WSTR,50)@[System::PackageName]&#8220; f\u00fcr den Paketnamen \u2013 oder zum Auswerten eigener Variablen. Die Formel darf keine Anf\u00fchrungszeichen (\u201c) enthalten.<\/li>\n<li>Optional die LineageID \u2013 dazu komme ich im Anschluss<\/li>\n<\/ul>\n<p>Hier der Code:<\/p>\n<blockquote><p>Private Sub addNewColumn2DerivedComponent(derivedColumnComponent As IDTSComponentMetaData100, name As String, dataType As Wrapper.DataType, dataTypeLaenge As Integer, expression As String, Optional lineageIDKeyColumn As Integer = 0)<br \/>\nDim neueSpalte As IDTSOutputColumn100 = derivedColumnComponent.OutputCollection(0).OutputColumnCollection.New()<br \/>\nneueSpalte.Name = name<br \/>\nneueSpalte.SetDataTypeProperties(dataType, dataTypeLaenge, 0, 0, 0)<br \/>\nneueSpalte.ExternalMetadataColumnID = 0<br \/>\nneueSpalte.ErrorRowDisposition = DTSRowDisposition.RD_IgnoreFailure<br \/>\nneueSpalte.TruncationRowDisposition = DTSRowDisposition.RD_IgnoreFailure<\/p>\n<p>Dim neueSpalteProp As IDTSCustomProperty100 = neueSpalte.CustomPropertyCollection.New()<br \/>\nneueSpalteProp.Name = &#8222;Expression&#8220;<br \/>\nIf lineageIDKeyColumn = 0 Then<br \/>\nneueSpalteProp.Value = expression<br \/>\nElse<br \/>\nneueSpalteProp.Value = &#8222;(DT_WSTR,&#8220; &amp; dataTypeLaenge &amp; &#8222;)#&#8220; + lineageIDKeyColumn.ToString()<br \/>\nEnd If<br \/>\nneueSpalteProp = neueSpalte.CustomPropertyCollection.New()<br \/>\nneueSpalteProp.Name = &#8222;FriendlyExpression&#8220;<br \/>\nneueSpalteProp.Value = expression<\/p>\n<p>End Sub<\/p><\/blockquote>\n<p>Hier einige Erkl\u00e4rungen dazu:<\/p>\n<ul>\n<li>Am Anfang wird die Spalte angelegt mit dem entsprechenden Namen und Datentyp<\/li>\n<li>Die derived column hat als Fehlertyp \u201cIgnore Failure\u201d, weil wir ja sonst die Idee der Quarant\u00e4ne ad absurdum f\u00fchren w\u00fcrden.<\/li>\n<li>Dann werden zwei Properties angelegt, die notwendig sind \u2013 die Expression und die \u201cfriendly expression\u201d. Mit der \u201cExpression\u201d rechnet SSIS, die \u201cfriendly expression\u201d wird angezeigt.<\/li>\n<\/ul>\n<p>Und jetzt die Geschichte mit der lineage-ID, wie versprochen:<\/p>\n<p>Man kann auch in den Formeln (expression) auf bestehende Spalten zugreifen. Das funktioniert \u00fcber die lineage-Id der Spalte, also beispielsweise so \u201c(DT_WSTR,50)#17\u201d. Die \u201cfriendly expression\u201d muss nicht unbedingt angegeben werden.<\/p>\n<p>Die lineage-ID kann man sich ermitteln, indem man alle Spalten des Inputs durchl\u00e4uft und den Namen der Spalte kennt. Allerdings kommt dabei der Wrapper f\u00fcr managed code zum Einsatz, den ich vorher bereits angedeutet habe. Wir ben\u00f6tigen n\u00e4mlich hier Designer-Funktionalit\u00e4t, wie sie im Visual Studio auch vorhanden ist. Deswegen sieht der Code etwas h\u00e4sslich aus:<\/p>\n<blockquote><p>Private Function findeSpalte(comp As IDTSComponentMetaData100, nameSpalte As String, ByRef nameSpalteGefunden As String) As Integer<br \/>\nDim inp As IDTSInput100 = comp.InputCollection(0)<br \/>\nDim virtualInp As IDTSVirtualInput100 = inp.GetVirtualInput()<br \/>\nDim virtualInpCols As IDTSVirtualInputColumnCollection100 = virtualInp.VirtualInputColumnCollection<br \/>\nDim designer As CManagedComponentWrapper = comp.Instantiate()<\/p>\n<p>nameSpalteGefunden = &#8222;&#8220;<\/p>\n<p>For Each virtualCol As IDTSVirtualInputColumn100 In virtualInpCols<br \/>\nIf virtualCol.Name = nameSpalte Then<br \/>\ndesigner.SetUsageType(inp.ID, virtualInp, virtualCol.LineageID, DTSUsageType.UT_READONLY)<br \/>\nnameSpalteGefunden = nameSpalte<br \/>\nReturn virtualCol.LineageID<br \/>\nEnd If<br \/>\nNext<\/p>\n<p>Return 0<br \/>\nEnd Function<\/p><\/blockquote>\n<p>Diese Funktion stellt auch den UsageType der Spalte auf Readonly. Dies erscheint zun\u00e4chst unn\u00f6tig, ohne dies funktioniert es\u00a0 aber nicht. Dies spiegelt wider, was der Advanced Editor f\u00fcr die Spalten anzeigt, die in den Formeln verwendet werden:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image5.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2015\/06\/image_thumb5.png\" alt=\"image\" width=\"673\" height=\"341\" border=\"0\" \/><\/a><\/p>\n<p>In diesem Beispiel wurde die EinrichtungID-Spalte in einer Formel verwendet.<\/p>\n<p>So habe ich die Prim\u00e4rschl\u00fcssel, die ich in der Quarant\u00e4ne-Tabelle mit protokollieren wollte, gefunden.<\/p>\n<h1>Die Derived Column mit der UNION ALL verbinden<\/h1>\n<p>Das funktioniert genauso wie der Pfad zur derived column, muss also nicht im Detail beschrieben werden.<\/p>\n<p>Damit sind wir nun fast fertig. Es sind lediglich ein paar Besonderheiten zu beachten \u2013 s. n\u00e4chster Blog-Eintrag.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nachdem wir im letzten Blog-Kapitel alle Komponenten eines Data Flows durchlaufen konnten und dazu erkennen konnten, ob eine Fehlerbehandlung m\u00f6glich und noch nicht vorhanden ist, m\u00fcssen wir nun die Fehlerbehandlung in diesen F\u00e4llen einbauen. Die Fehlerbehandlung soll wie folgt funktionieren: Fehlerbehandlung auf Redirect Row einstellen Den Error Output in eine neu zu erstellende Derived Column &hellip; <a href=\"https:\/\/www.csopro.de\/biblog\/2015\/06\/quarantne-teil-3-anlegen-der-zustzlichen-komponenten\/\" class=\"more-link\"><span class=\"screen-reader-text\">Quarant&auml;ne-Teil 3: Anlegen der zus&auml;tzlichen Komponenten<\/span> weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,5,38],"tags":[27,18,19],"class_list":["post-347","post","type-post","status-publish","format-standard","hentry","category-integrationservices","category-projekte","category-visualbasicdotnet","tag-data-flow","tag-error-handling","tag-quarantaene"],"_links":{"self":[{"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/posts\/347","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/comments?post=347"}],"version-history":[{"count":4,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/posts\/347\/revisions"}],"predecessor-version":[{"id":353,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/posts\/347\/revisions\/353"}],"wp:attachment":[{"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/media?parent=347"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/categories?post=347"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/tags?post=347"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}