【问题标题】:SSIS 2012 - Variable empty on second script executionSSIS 2012 - 第二次执行脚本时变量为空
【发布时间】:2019-03-22 04:46:23
【问题描述】:

我在数据流中有一个脚本组件转换。 在这个脚本组件中,我从一个对象变量中读取了一个表。通过脚本的第一条记录工作正常。变量正确读取并完美加载到列表对象中。

下一条记录传递到脚本中出现问题。

查看它报告的记录计数为 44 的变量,当它尝试加载到我的列表中时,我得到一个 rowcount = 0

下面是加载列表的脚本

    List<PublicHoliday> PublicHolidays = new List<PublicHoliday>();


    OleDbDataAdapter A = new OleDbDataAdapter();
    DataTable dt = new DataTable();
    A.Fill(dt, Variables.LISTPublicHolidays);

    foreach (DataRow row in dt.Rows)
    {
        object[] array = row.ItemArray;
        var Public = new PublicHoliday()
        {
            DateKey = int.Parse(array[0].ToString()),
            FullDateAlternateKey = DateTime.Parse(array[1].ToString())
        };
        PublicHolidays.Add(Public);
    }

我错过了什么吗?有没有人遇到过这个问题?

【问题讨论】:

  • 代码在什么方法,即PreExecute、Input0_ProcessInputRow、PostExecute?
  • @userfl89 答案中提到的内容是正确的。问题是对象变量contains a Recordset 在使用 Fill() 方法使用时需要关闭并重新打开,否则多次执行时将返回空结果。与代码执行的方法无关
  • @Yahfoufi 我明白了,这是有道理的。谢谢
  • 检查我的答案更新我添加了一个新的可能的解决方案

标签: c# sql-server ssis etl ssis-2012


【解决方案1】:

试图找出问题

来自以下OleDbDataAdapter.Fill Method (DataTable, Object) documentation

注意
将 ADO Recordset 或 Record 对象与 .NET Framework 应用程序结合使用时,请务必在完成后调用 Close。这可以确保及时释放与数据源的底层连接,并且还可以防止由于在现有引用仍然存在时被垃圾回收回收非托管 ADO 对象而可能导致的访问冲突。

也指Filling a DataSet with an ADO Recordset or Record - documentation

请注意,当 Fill 操作完成时,采用 DataSet 和 ADO 对象的 OleDbDataAdapter.Fill 重载会隐式调用 ADO 对象上的 Close。在调用采用 DataTable 的 OleDbDataAdapter.Fill 重载后,您需要显式关闭 ADO Recordset 或 Record 对象。

这意味着在 RecordSet 上调用 Fill 方法时,您必须在第二次使用它之前关闭它,否则不会返回任何行。


可能的解决方法

我真的不知道如何从 Object 变量中关闭 Recordset,但我会尝试提供一些可能的解决方法:

(1) 保存到数据表

在下面的链接中,他们提到了以下解决方法:

  1. 一旦我的 Recordset @[User::FilePath] 被填充,我就使用 Script Task ,并使用 OledbDataAdapter 将其填充到 DataSet ds 中 数据表。

  2. 然后,在同一个脚本任务中,我把 ds 的值放到一个新的 对象类型@[User::FilePathDataTable] 的变量。

通过这样做,FilePathDataTable 的 DataType 变为 System.Data.DataTable.

这个数据表可以很容易地在 For-Each 循环。

我不在 ssis 的 ForEach 循环中使用 DataAdapter.Fill() 方法 现在。我只是将@[User::FilePathDataTable] 的值分配给一个新的 数据集,并将其用于迭代。

参考

(2) 使用记录集源

尝试使用 RecordSet Source 来代替使用脚本组件从 Object 变量生成行。

(3) 将变量转换为记录集

我没有测试过这种方法,我不确定它是否适用于对象变量

它需要对 Microsoft ActiveX 数据对象的引用。

List<PublicHoliday> PublicHolidays = new List<PublicHoliday>();

var rs = ((ADODB.Recordset)Variables.LISTPublicHolidays);
OleDbDataAdapter A = new OleDbDataAdapter();
DataTable dt = new DataTable();
A.Fill(dt, rs);

foreach (DataRow row in dt.Rows)
{
    object[] array = row.ItemArray;
    var Public = new PublicHoliday()
    {
        DateKey = int.Parse(array[0].ToString()),
        FullDateAlternateKey = DateTime.Parse(array[1].ToString())
    };
    PublicHolidays.Add(Public);
}
rs.Close();

更新 1

基于下面的 cmets 尝试从第一个脚本中删除 rs.Close(); 方法,并在执行 Fill 方法之前在第二个脚本中使用 rs.MoverFirst() 方法以便能够从记录集中检索信息。

根据以下链接删除了第三种方法:

【讨论】:

  • 嗨,哈迪,感谢您为这个问题所做的所有工作。使用您的第三种解决方案,我至少会遇到一个可行的错误 System.Runtime.InteropServices.COMException: 'Operation is not allowed when the object is closed.'
  • @Caz1224 为什么不尝试第一种方法,我曾经遇到过类似的问题,我能够使用这种方法解决它。关于第三种方法,我尝试使用连接检索的记录集,我并没有真正尝试使用对象变量。
  • @Caz1224 关于第三种方法,尝试从第一个脚本中删除rs.Close() 命令,并在第二个脚本中添加Rs.MoveFirst(),然后开始读取对象变量
  • 我最终选择了不同的方式。 ADO 方法只是造成了太多的痛苦。我最终做的是一个 TO JSON SQL 查询,将其保存为一个对象,将其转换为字符串,然后将其反序列化为我在脚本组件中的列表
  • 方法 #1 对我有用!从未想过会如此简单。
【解决方案2】:

您应该使用 ADO NET 而不是 Ole Db 连接:

Dim dt As Data.DataTable
Dim ds As Data.DataSet = CType(Dts.Variables("Recordset").Value, DataSet)
dt = ds.Tables(0)

参考:https://www.sqlshack.com/execute-sql-tasks-in-ssis-output-parameters-vs-result-sets

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-15
    • 2023-04-01
    • 1970-01-01
    • 2017-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多