【问题标题】:SSIS Programmatic Package Validate/Run IssueSSIS 程序包验证/运行问题
【发布时间】:2016-12-09 09:41:02
【问题描述】:

我已经在 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 以通过编程方式获取此类验证错误?**

感谢任何帮助!

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

// 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

【问题讨论】:

    标签: c# sql-server ssis


    【解决方案1】:

    以下是我查看您的代码的想法:简而言之,我认为您缺少在目标组件中的输入列和外部列之间建立关联的步骤。

    对于您的目标组件,在您分配 SqlCommand 后:接下来的三行代码(AcquireConn、Reinit Meta 和 Close Conn)实质上是从您的 oracle 数据库中获取 sqlcommand 的元数据。此元数据将是列的名称及其关联的数据类型。然后,此信息将存储在目标组件的 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 数据类型)应该匹配。

    【讨论】:

    • 在第一行代码中,我假设您的意思是 destInput.InputColumnCollection?因为那绝对有效!我将此标记为答案,但如果您可以扩展您的学习方式,我将不胜感激。我已经阅读了 MSDN 上的文档,要么它们稀疏且难以掌握,要么我很密集,我真的很想知道哪个!
    • 是的..在第一行代码中,它应该是destInput。
    • 就学习而言,主要是通过阅读文档,对其进行编码并观察dtsx的xml中的变化。很快你就会发现所有 Pipeline 组件(无论是查找、合并还是源等)都遵循相同的配置设计原则。我正在整理一些笔记,会让你知道。同时,如果您有任何其他 ssis 程序化问题,请告知。
    • 我一直在诉诸将 xml 从我在 BIDS 中制作的“已知良好”包与我的程序包中的差异...很高兴知道我没有错过任何魔法:P 再次感谢!
    【解决方案2】:

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

    我怀疑 SQL Server 查询中的“Select *”没有被解释为正确的数据类型。如果我没记错的话,直到它输入到目标时才会显示错误,因为它不是与目标列匹配的有效数据类型。

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

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

    【讨论】:

    • “select *”在 Oracle(目标)查询中,SSIS 显然对此很满意。我同意这看起来很奇怪。我也想到了 NUMBER 与 INT 的业务,但 NUMBER 正在接收 INT 值,所以我认为没关系(int 本质上是一个子集); SSIS 显然同意。无论如何谢谢:)
    猜你喜欢
    • 1970-01-01
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多