以编程方式编写SSIS包 - 动态XML到OLE DB

4
我一直在努力了解其他用户如何完成他们的项目,但我的理解仍然有限。
我想要将任何给定的XML源转换为数据流任务,并将其数据传递到与XML文件表名匹配的OLE DB目标。使用可视化工具运行它意味着我无法进行动态数据流任务,因为元数据不会刷新。
我创建了一个脚本来创建一个包,但是当我在Visual Studio中打开该包时,它会显示红色的X,表示不能有零个输入列。当我深入挖掘并查看OLE DB目标的映射,然后单击确定 - 它会为我进行更正。我无法弄清楚如何以编程方式执行此操作。
我已经看到其他人通过使用foreach循环并遍历输入列来解决这个问题,但我似乎无法弄清楚其中的细节。
我还有一个单独的脚本,我试图模仿几个人的脚本,但它有不同的问题。不确定如何将其作为附件发布。
非常感谢您的帮助 :)
编辑
我一直在尝试BIML,并得到了积极的反馈,我会继续尝试...但是在短期内,我想知道是否有人可以帮助我弄清楚为什么这不会填充我的输入的ExternalMetaDataColumnId。我在下面发布了我的更新代码,其中包含未能按预期工作的foreach循环。
谢谢
namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            #region Initial Setup
            Application a = new Application();
            Package p = new Package();
            TaskHost t = p.Executables.Add("DTS.Pipeline") as TaskHost;
            t.Name = "DataFlow Task";
            t.Description = "Flat File to Database";
            MainPipe mp = t.InnerObject as MainPipe;
            #endregion

            #region Flat File Source in Dataflow Task
            IDTSComponentMetaData100 md = mp.ComponentMetaDataCollection.New(); 
            md.ComponentClassID = "Microsoft.XmlSourceAdapter"; 
            md.Name = "XML Source"; 
            CManagedComponentWrapper wrp = md.Instantiate(); 
            wrp.ProvideComponentProperties(); 

            #endregion

            #region Add connection manager to OLE DB
            ConnectionManager conn = p.Connections.Add("OLEDB");
            conn.Name = "westcoastuserDBO";
            conn.ConnectionString = "Data Source=SERVER;Initial Catalog=DBO;Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;";
            #endregion


            #region XML Source Properties
            wrp.SetComponentProperty("XMLData", @"C:\Users\file.xml");
            wrp.SetComponentProperty("XMLSchemaDefinition", @"C:\Users\file.xsd");
            wrp.SetComponentProperty("AccessMode", 0);
            wrp.SetComponentProperty("UseInlineSchema", false);
            //below does not work
            //wrp.SetComponentProperty("XMLIntegerMapping", 0).TypeConverter = "Microsoft.SqlServer.Dts.Pipeline.XmlSourceAdapter + XMLIntegerMappingConverter";

            wrp.ReinitializeMetaData();
            wrp.ReleaseConnections();
            IDTSComponentMetaData100 md2 = mp.ComponentMetaDataCollection.New();
            md2.ComponentClassID = "Microsoft.OLEDBDestination";
            CManagedComponentWrapper wrp2 = md2.Instantiate();
            wrp2.ProvideComponentProperties();
            md2.Name = "OLE DB Connection";
            md2.UsesDispositions = true;
            md2.Version = 4;
            wrp2.SetComponentProperty("OpenRowset", "dbo.authorizations");
            #endregion

            IDTSPath100 path = mp.PathCollection.New();
            path.AttachPathAndPropagateNotifications(md.OutputCollection[0], md2.InputCollection[0]);

            IDTSInput100 input = md2.InputCollection[0];
            IDTSVirtualInput100 vInput = input.GetVirtualInput();

            //below taken from https://dev59.com/TmjWa4cB1Zd3GeqPutqA
            IDTSExternalMetadataColumnCollection100 externalColumnCollection = input.ExternalMetadataColumnCollection;


            // Iterate through the virtual input column collection.
            foreach (IDTSVirtualInputColumn100 vColumn in vInput.VirtualInputColumnCollection)
            {
                // Call the SetUsageType method of the destination
                // to add each available virtual input column as an input column.
                wrp2.SetUsageType(
                   input.ID, vInput, vColumn.LineageID, DTSUsageType.UT_READONLY);
            }

            // Get the destination's default output collection
            IDTSOutputCollection100 outColl = md2.OutputCollection;

            // Iterate through the outputs in default output collection
            foreach (IDTSOutput100 output in outColl)
            {
                // Iterate through the default output columns in the output 
                int count = output.OutputColumnCollection.Count;
                foreach (IDTSOutputColumn100 outputColumn in output.OutputColumnCollection)
                {
                    // Get the output's external metadata column collection
                    IDTSExternalMetadataColumnCollection100 extMetadataColumnColl = output.ExternalMetadataColumnCollection;

                    // Iterate through the external metadata column collection's external metadata columns
                    foreach (IDTSExternalMetadataColumn100 extMetadataColumn in extMetadataColumnColl)
                    {
                        // Call the MapOutPutColumn method of the destination to map
                        // each available output column to an external metadata column 
                        wrp2.MapOutputColumn(
                            output.ID, outputColumn.ID, extMetadataColumn.ID, true);
                    }
                }
            }

            md2.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface(conn);
            md2.RuntimeConnectionCollection[0].ConnectionManagerID = conn.ID;
            conn.AcquireConnection(null);




            #region Save Package to FileSystem
            string packageXml = @"C:\Users\test.dtsx";
            XmlDocument myPkgDocument = new XmlDocument();
            p.SaveToXML(ref myPkgDocument, null, null);
            a.SaveToXml(packageXml, p, null);
            #endregion
        }
    }
}

1
使用BIML吧,天哪 :) http://www.bimlscript.com/ 和 https://www.mssqltips.com/sqlservertip/3094/introduction-to-business-intelligence-markup-language-biml-for-ssis/ 以及 http://www.sqlservercentral.com/stairway/100550/ - 通过使用他们自己的应用程序,您可以绕过BIDShelper。 - SqlKindaGuy
1
也可以看看这个链接。非常实用的指南,几乎就是你要做的事情。http://www.sqlservercentral.com/articles/Integration+Services+(SSIS)/130470/?utm_source=SSC&utm_medium=pubemail - SqlKindaGuy
哦,太棒了。我一定会把这个放在我的尝试列表中。我已经对我的C#脚本感到厌烦了,而BIML现在看起来非常吸引人。理想情况下,我只需要一个加载包以刷新元数据的脚本...就这样。不幸的是,似乎通过BIML脚本创建整个包比使用C#更容易。感谢您的文章! :) - Benjamin Jedd
1个回答

1
我认为问题在于您没有将输入列映射到OLEDB目标,如果您打开包并单击OLEDB目标并转到映射部分,则它将根据名称自动映射列。其他人使用的Foreach循环是为了循环遍历列并将其映射到相关目标列。
有许多文章谈论动态创建SSIS包,您可以参考它们获取更多信息:

1
除了您的答案之外,我建议如果他想动态创建SSIS包,则使用BIML。http://www.bimlscript.com/ - SqlKindaGuy
@PlaidDK 我同意这个观点。 - Hadi
我暂时不想将这个标记为答案,因为在发布问题之前,我已经访问了所有页面,除了其中一个页面。我尝试运行一个foreach循环,它通过IDTSInputColumn100列并分配ExternalMetadataColumnId,但它没有成功映射输入列,并最终返回错误,即“输入列不能为零”。今天我会花些时间来查看最后一页,并使用其示例作为运行输入列以验证外部元数据的方法。如果不行,我将尝试@PlaidDK的建议,使用BIML(?)。 - Benjamin Jedd
@BenjaminJedd 如果你熟悉BIML,实现这个就更容易了。 - Hadi
1
我的同事和我刚尝试了使用 BIML(安装和查找补丁真的很可怕),似乎只能运行一次,除非我学会如何编写 C# 脚本以动态地完成最初想要使用 BIML 完成的任务。我需要一个单一的程序包,连续运行,逐个表格拉取数据,然后按表名动态将其加载到数据库中。C# 脚本应该加载一个独立的程序包并更新元数据,因为 VS 无法自行完成这项工作。虽然易于使用和有趣,但是对于这个问题,BIML 似乎不是解决方案。 - Benjamin Jedd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接