SSIS程序包验证/运行问题

时间:2016-12-08 18:31:51

标签: c# sql-server ssis

我已在MSDN [https://social.msdn.microsoft.com/Forums/en-US/6cd447d8-21e5-44be-aee6-ad6bdcaac40f/programmatically-created-package-having-difficulties-running?forum=sqlintegrationservices]上发布了同样的问题,我希望有人可以提供帮助。

---这里是问题---

我正在尝试创建一个简单的SSIS任务,通过.NET Interop程序集将数据从SQL Server表(带有一个名为[num]的int列)加载到Oracle表(带有一个名为[ID]的NUMBER列)对于SSIS。我可以创建包并将其保存到DTSX文件,但是当我运行时(通过编程方式或通过dtexec),它会失败。我按照编程方式创建包的开发指南,当我浏览各种对象的属性时,我没有真正跳出来。

作为次要问题,我看到有一个Package.Validate方法可用,但我不知道如何调用它(需要参数和返回值)。当我通过dtexec运行包时,它会进行验证并提供一些有用的验证错误,所以我希望能够以编程方式挂钩。

我得到的验证错误是:

  

错误:2016-12-08 09:39:15.96代码:0xC004706B来源:   {A8E8D1A6-3826-4222-B6DC-46008A1722DF} SSIS.Pipeline描述:   " OLE DB Destination"验证失败并返回验证状态   " VS_NEEDSNEWMETADATA"

从我在网上可以看出,这个错误的原因是列映射不好,但是我已经或多或少直接从开发指南中提取了代码。

所以我的两个问题是:

  1. 如何解决此验证错误VS_NEEDSNEWMETADATA,最好是以编程方式解决?
  2. 如何以编程方式调用Package.Validate以获取此类验证错误?**
  3. 感谢任何帮助!

    完整代码清单(我使用Visual Studio在C#Interactive中运行此代码,不会抛出任何异常,但结果会以失败值结束):

    // these are just adding references to the context:
    #r "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SqlServer.DTSPipelineWrap\v4.0_11.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.DTSPipelineWrap.dll"
    #r "C:\Windows\Microsoft.NET\assembly\GAC_64\Microsoft.SqlServer.DTSRuntimeWrap\v4.0_11.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.DTSRuntimeWrap.dll"
    #r "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SqlServer.ManagedDTS\v4.0_11.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ManagedDTS.dll"
    #r "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SqlServer.PipelineHost\v4.0_11.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.PipelineHost.dll"
    #r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.dll"
    #r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Core.dll"
    #r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Xml.Linq.dll"
    #r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.DataSetExtensions.dll"
    #r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Microsoft.CSharp.dll"
    #r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Data.dll"
    #r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Net.Http.dll"
    #r "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Xml.dll"
    // the actual code:
    
    using dtsrt = Microsoft.SqlServer.Dts.Runtime;
    using dtsp = Microsoft.SqlServer.Dts.Pipeline;
    using dtspw = Microsoft.SqlServer.Dts.Pipeline.Wrapper;
    using refl = System.Reflection;
    private string GetHresultSymbolicName(int errorCode)
    {
        dtsrt.HResults hresults = new Microsoft.SqlServer.Dts.Runtime.HResults();
        return (from refl.FieldInfo fi in hresults.GetType().GetFields() where (((int)fi.GetValue(hresults)) == errorCode) select fi.Name).Single();
    }
    
    // base setup
    dtsrt.Package package = new dtsrt.Package();
    dtsrt.Executable e = package.Executables.Add("STOCK:PipelineTask");
    dtsrt.TaskHost thMainPipe = e as dtsrt.TaskHost;
    dtspw.MainPipe dataFlowTask = thMainPipe.InnerObject as dtspw.MainPipe;
    dtsrt.Application app = new Microsoft.SqlServer.Dts.Runtime.Application();
    dtsrt.PipelineComponentInfos componentInfos = app.PipelineComponentInfos;
    
    // source connection
    dtsrt.ConnectionManager srcConnectionManager = package.Connections.Add("OLEDB");
    srcConnectionManager.Name = "Source OLEDB Connection";
    srcConnectionManager.ConnectionString = "Provider=SQLOLEDB; Address=XXX; Database=Sandbox; Trusted_Connection=yes;";
    
    // dest connection
    dtsrt.ConnectionManager destConnectionManager = package.Connections.Add("OLEDB");
    destConnectionManager.Name = "Destination OLEDB Connection";
    destConnectionManager.ConnectionString = "Provider=OraOLEDB.Oracle; Data Source=XXX;User Id=SANDBOX;Password=123456;";
    // thanks: ole.OleDbDataReader rdr = ole.OleDbEnumerator.GetRootEnumerator();
    
    // dest component
    dtspw.IDTSComponentMetaData100 destComponent = dataFlowTask.ComponentMetaDataCollection.New();
    destComponent.ComponentClassID = "DTSAdapter.OLEDBDestination.3";
    destComponent.Instantiate();
    dtspw.CManagedComponentWrapper destWrapper = destComponent.Instantiate();
    destWrapper.ProvideComponentProperties();
    destComponent.RuntimeConnectionCollection[0].ConnectionManager = dtsrt.DtsConvert.GetExtendedInterface(destConnectionManager);
    destComponent.RuntimeConnectionCollection[0].ConnectionManagerID = destConnectionManager.ID;
    destComponent.CustomPropertyCollection["CommandTimeout"].Value = 0; // no timeout
    destComponent.CustomPropertyCollection["AccessMode"].Value = 2; // sqlcommand
    destComponent.CustomPropertyCollection["SqlCommand"].Value = "select * from my_asdf";
    destWrapper.AcquireConnections(null);
    destWrapper.ReinitializeMetaData();
    destWrapper.ReleaseConnections();
    
    // source component
    dtspw.IDTSComponentMetaData100 srcComponent = dataFlowTask.ComponentMetaDataCollection.New();
    srcComponent.ComponentClassID = "DTSAdapter.OLEDBSource.3";
    srcComponent.Instantiate();
    dtspw.CManagedComponentWrapper srcWrapper = srcComponent.Instantiate();
    srcWrapper.ProvideComponentProperties();
    srcComponent.RuntimeConnectionCollection[0].ConnectionManager = dtsrt.DtsConvert.GetExtendedInterface(srcConnectionManager);
    srcComponent.RuntimeConnectionCollection[0].ConnectionManagerID = srcConnectionManager.ID;
    srcComponent.CustomPropertyCollection["CommandTimeout"].Value = 0; // no timeout
    srcComponent.CustomPropertyCollection["AccessMode"].Value = 2; // sqlcommand
    srcComponent.CustomPropertyCollection["SqlCommand"].Value = "select num from big_numbers2";
    srcWrapper.AcquireConnections(null);
    srcWrapper.ReinitializeMetaData();
    srcWrapper.ReleaseConnections();
    
    dtspw.IDTSPath100 path = dataFlowTask.PathCollection.New();
    path.AttachPathAndPropagateNotifications(srcComponent.OutputCollection[0], destComponent.InputCollection[0]);
    
    // Just one column mapping, as each table has just one column
    dtspw.IDTSInput100 destInput = destComponent.InputCollection[0];
    dtspw.IDTSVirtualInput100 vDestInput = destInput.GetVirtualInput();
    destWrapper.SetUsageType(destInput.ID, vDestInput, vDestInput.VirtualInputColumnCollection[0].LineageID, dtspw.DTSUsageType.UT_READONLY);
    
    // save it, run it
    app.SaveToXml("D:\\myDtsx.dtsx", package, null);
    dtsrt.DTSExecResult result = package.Execute(); // result = failure
    

2 个答案:

答案 0 :(得分:0)

您是否尝试在文本编辑器中打开.dtsx文件并查看包中的元数据信息?有什么突出的吗?

我怀疑是"选择*"在SQL Server查询中没有被解释为正确的数据类型。如果我没有弄错,错误将不会显示在输入目的地之前,因为它不是与目标列匹配的有效数据类型。

Oracle NUMBER也与INT完全相同。请查看本指南,了解您是否不必在程序包中添加步骤以在SQL Server和Oracle之间执行数据类型转换任务。 http://docs.oracle.com/cd/B19306_01/gateways.102/b14270/apa.htm

希望这两件事中的一件可以帮助您解决问题..

答案 1 :(得分:0)

以下是我从您的代码中看到的内容:简而言之,我认为您错过了在目标组件中的输入列和外部列之间建立关联的步骤。

对于目标组件,在分配SqlCommand之后:接下来的三行代码(AcquireConn,Reinit Meta和Close Conn)基本上从oracle数据库获取sqlcommand的元数据。此metada将是列的名称及其关联的数据类型。然后,此信息将存储在目标组件的ExternalMetadataColumn集合中。现在在目标组件中,一个"列映射"在ExternalMetadataColumn集合中的列与目标组件的InputColumn集合中的列关联时建立。

在代码中,在目标组件上进行元数据调用后,您将创建源组件。因此,当创建目标组件的外部元数据时,它没有任何输入列来关联这些外部元数据列。

在代码中,在创建源组件并附加源和目标之后,您将调用GetVirtualInput,然后使用UT_READONLY调用SetUsageType。使用UT_READONLY调用SetUsageType后,目标组件中将有一个输入列,您可以将其与外部元数据列关联。

因此,在调用SetUsageType之后,现在在目标的设计时组件上调用MapInputColumn方法,并传入要匹配的输入,输入列和外部元数据列的id。

所以假设你要映射" num"从您的源组件到名为" my_external_column"的外部列的列,然后您将执行此操作:

IDTSInputColumn100 inputColumn = destInputCols.InputColumnCollection["num"];

IDTSExternalMetadataColumn100 externalColumn = destInput.ExternalMetadataColumnCollection["my_external_column"];

destWrapper.MapInputColumn(destInput.ID, inputColumn.ID, externalColumn.ID)

您应该能够看到正确映射的列"正确"。要使映射成功,数据类型(特别是SSIS数据类型)应匹配。