当作为循环的一部分从事件调用时,ScriptBuffer 抛出 NullReferenceException - SSIS 脚本组件
ScriptBuffer throws NullReferenceException when called from an Event as part of a loop - SSIS Script Component
我有一个 SSIS 包,它调用 Data Flow Task
作为循环的一部分,该循环迭代不同的端点地址(超出范围)。
Data Flow Task
有一个源 Script Component
负责调用 REST API 并为每个结果创建一行。
有3个输出缓冲区;
1.实际数据行
2.错误行
3.监控
用于遥测的监视缓冲区,并通过每次 API 发出请求时触发的事件 (EventHander
) 进行填充。
在 ForEach
int Control Flow
循环的第一次迭代期间,一切都按预期运行,所有缓冲区都生成正确的行。
但是,在下一次迭代期间,填充在事件中的监视缓冲区抛出;
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.HandleUserException(Exception e)
at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.PrimeOutput(Int32 outputs, Int32[] outputIDs, PipelineBuffer[] buffers)
at Microsoft.SqlServer.Dts.Pipeline.ManagedComponentHost.HostPrimeOutput(IDTSManagedComponentWrapper100 wrapper, Int32 outputs, Int32[] outputIDs, IDTSBuffer100[] buffers, IntPtr ppBufferWirePacket)
我不明白为什么 MonitoringBuffer
在后续迭代中没有初始化。
调用MonitoringBuffer.AddRow();
时出现异常。
这是为了便于阅读而简化的整个脚本组件:
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
private string ClientCode { get { return Variables.ErplyClientCode; } }
private string Username { get { return Variables.ErplyUsername; } }
private string Password { get { return Variables.ErplyPassword; } }
private bool IsTest { get { return Variables.IsTest; } }
private int ErplyRecordsPerPage { get { return Variables.ErplyRecordsPerPage; } }
private string ErplyDebugOutputPath { get { return Variables.ErplyDebugOutputPath; } }
private DateTime ChangeSince { get { return Variables.ChangeSince; } }
private int records { get; set; }
private int errors { get; set; }
private string rawFolder { get; set; }
public override void PreExecute()
{
base.PreExecute();
}
public override void PostExecute()
{
base.PostExecute();
}
public override void CreateNewOutputRows()
{
ErplyAPI.OnPreRequestEvent += new EventHandler<EAPIEvent>(ErplyAPI_OnPreRequestEvent);
var staff = ErplyAPI.getStaff(ClientCode, Username, Password, ChangeSince, ErplyRecordsPerPage, IsTest);
foreach (var p in staff.List)
{
try
{
if (!p.IsError)
{
EmployeeBuffer.AddRow();
EmployeeBuffer.employeeID = p.employeeID;
}
else
{
ErrorBuffer.AddRow();
ErrorBuffer.employeeID = p.employeeID;
ErrorBuffer.Error = p.Error.Message.Trim() + "\n" + p.Error.StackTrace;
errors++;
}
records++;
}
catch (Exception ex)
{
this.ComponentMetaData.FireWarning(0, "Script", ex.Message + "\n" + ex.StackTrace, string.Empty, 0);
}
}
EmployeeBuffer.SetEndOfRowset();
ErrorBuffer.SetEndOfRowset();
}
private void ErplyAPI_OnPreRequestEvent(object sender, EAPIEvent e)
{
var request = string.Empty;
var sessionKey = string.Empty;
bool fireAgain = true;
if (e == null)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("EAPIEvent is NULL in ErplyAPI_OnPreRequestEvent. Amonit did not log the Erply request."), string.Empty, 0);
return;
}
if (e.eAPI == null)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("EAPIEvent.eAPI is NULL in ErplyAPI_OnPreRequestEvent. Amonit did not log the Erply request."), string.Empty, 0);
return;
}
try
{
if (e.Parameters != null && e.Parameters.ContainsKey("request"))
request = e.Parameters["request"].ToString();
if (request != "verifyUser" && e.Parameters != null && e.Parameters.ContainsKey("sessionKey"))
sessionKey = e.Parameters["sessionKey"].ToString();
}
catch (Exception ex)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("Error occurred assigning variables from EAPIEvent parameters in ErplyAPI_OnPreRequestEvent. {0} {1}", ex.Message, ex.StackTrace), string.Empty, 0);
}
try
{
MonitoringBuffer.AddRow(); // Exception occurs here
MonitoringBuffer.Request = ResizeString(request, 255);
MonitoringBuffer.SessionKey = ResizeString(sessionKey, 128);
}
catch (Exception ex)
{
var message = string.Format("Error occurred outputting Erply request in ErplyAPI_OnPreRequestEvent. {0} {1}", ex.Message, ex.StackTrace);
MonitoringBuffer.ErrorMessage = ResizeString(message, 8000);
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", message, string.Empty, 0);
}
finally
{
MonitoringBuffer.EndOfRowset();
}
}
}
我解决了问题。
从事件访问变量分配器时引发异常。由于某种原因, GetValueWithContext(ScriptComponent.EvaluatorContext)
在第二次调用期间被删除。为什么会发生这种情况超出了我的范围。
解决方案很简单,将变量分配器中的变量分配给局部 属性 或 OnPreExecute
函数中的变量。
最好不要在 CreateNewOutputRows
中调用变量分配器,因为它会导致变量锁定。
我也 运行 解决了这个问题,但我的解决方案有点不同——将变量赋值移动到 PreExecute()
没有帮助。
相反,我所做的是我想解析三个不同的文件,并使用脚本组件读取每个文件。他们的专栏有点相似,所以我创建了一个数据流任务,确保它有效,然后复制它并修改每个副本以反映文件中的差异。 运行 每个单独的数据流任务都成功了,但是当我尝试 运行 其中两个时,一个接一个地循环,我在 HostPrimeOutput()
之后得到了一个 NullReferenceException
在我的脚本组件中调用 OutputBuffer.AddRow()
方法。
事实证明,当我复制每个数据流任务时,脚本组件都保持相同的命名空间,我猜它不喜欢这样。所以,我创建了 b运行d 个新脚本组件,再次设置所有输出列(呃!),将脚本主体复制过来,很高兴。
我有一个 SSIS 包,它调用 Data Flow Task
作为循环的一部分,该循环迭代不同的端点地址(超出范围)。
Data Flow Task
有一个源 Script Component
负责调用 REST API 并为每个结果创建一行。
有3个输出缓冲区; 1.实际数据行 2.错误行 3.监控
用于遥测的监视缓冲区,并通过每次 API 发出请求时触发的事件 (EventHander
) 进行填充。
在 ForEach
int Control Flow
循环的第一次迭代期间,一切都按预期运行,所有缓冲区都生成正确的行。
但是,在下一次迭代期间,填充在事件中的监视缓冲区抛出;
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.HandleUserException(Exception e)
at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.PrimeOutput(Int32 outputs, Int32[] outputIDs, PipelineBuffer[] buffers)
at Microsoft.SqlServer.Dts.Pipeline.ManagedComponentHost.HostPrimeOutput(IDTSManagedComponentWrapper100 wrapper, Int32 outputs, Int32[] outputIDs, IDTSBuffer100[] buffers, IntPtr ppBufferWirePacket)
我不明白为什么 MonitoringBuffer
在后续迭代中没有初始化。
调用MonitoringBuffer.AddRow();
时出现异常。
这是为了便于阅读而简化的整个脚本组件:
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
private string ClientCode { get { return Variables.ErplyClientCode; } }
private string Username { get { return Variables.ErplyUsername; } }
private string Password { get { return Variables.ErplyPassword; } }
private bool IsTest { get { return Variables.IsTest; } }
private int ErplyRecordsPerPage { get { return Variables.ErplyRecordsPerPage; } }
private string ErplyDebugOutputPath { get { return Variables.ErplyDebugOutputPath; } }
private DateTime ChangeSince { get { return Variables.ChangeSince; } }
private int records { get; set; }
private int errors { get; set; }
private string rawFolder { get; set; }
public override void PreExecute()
{
base.PreExecute();
}
public override void PostExecute()
{
base.PostExecute();
}
public override void CreateNewOutputRows()
{
ErplyAPI.OnPreRequestEvent += new EventHandler<EAPIEvent>(ErplyAPI_OnPreRequestEvent);
var staff = ErplyAPI.getStaff(ClientCode, Username, Password, ChangeSince, ErplyRecordsPerPage, IsTest);
foreach (var p in staff.List)
{
try
{
if (!p.IsError)
{
EmployeeBuffer.AddRow();
EmployeeBuffer.employeeID = p.employeeID;
}
else
{
ErrorBuffer.AddRow();
ErrorBuffer.employeeID = p.employeeID;
ErrorBuffer.Error = p.Error.Message.Trim() + "\n" + p.Error.StackTrace;
errors++;
}
records++;
}
catch (Exception ex)
{
this.ComponentMetaData.FireWarning(0, "Script", ex.Message + "\n" + ex.StackTrace, string.Empty, 0);
}
}
EmployeeBuffer.SetEndOfRowset();
ErrorBuffer.SetEndOfRowset();
}
private void ErplyAPI_OnPreRequestEvent(object sender, EAPIEvent e)
{
var request = string.Empty;
var sessionKey = string.Empty;
bool fireAgain = true;
if (e == null)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("EAPIEvent is NULL in ErplyAPI_OnPreRequestEvent. Amonit did not log the Erply request."), string.Empty, 0);
return;
}
if (e.eAPI == null)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("EAPIEvent.eAPI is NULL in ErplyAPI_OnPreRequestEvent. Amonit did not log the Erply request."), string.Empty, 0);
return;
}
try
{
if (e.Parameters != null && e.Parameters.ContainsKey("request"))
request = e.Parameters["request"].ToString();
if (request != "verifyUser" && e.Parameters != null && e.Parameters.ContainsKey("sessionKey"))
sessionKey = e.Parameters["sessionKey"].ToString();
}
catch (Exception ex)
{
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", string.Format("Error occurred assigning variables from EAPIEvent parameters in ErplyAPI_OnPreRequestEvent. {0} {1}", ex.Message, ex.StackTrace), string.Empty, 0);
}
try
{
MonitoringBuffer.AddRow(); // Exception occurs here
MonitoringBuffer.Request = ResizeString(request, 255);
MonitoringBuffer.SessionKey = ResizeString(sessionKey, 128);
}
catch (Exception ex)
{
var message = string.Format("Error occurred outputting Erply request in ErplyAPI_OnPreRequestEvent. {0} {1}", ex.Message, ex.StackTrace);
MonitoringBuffer.ErrorMessage = ResizeString(message, 8000);
ComponentMetaData.FireWarning(0, "SC_ERPLY_API", message, string.Empty, 0);
}
finally
{
MonitoringBuffer.EndOfRowset();
}
}
}
我解决了问题。
从事件访问变量分配器时引发异常。由于某种原因, GetValueWithContext(ScriptComponent.EvaluatorContext)
在第二次调用期间被删除。为什么会发生这种情况超出了我的范围。
解决方案很简单,将变量分配器中的变量分配给局部 属性 或 OnPreExecute
函数中的变量。
最好不要在 CreateNewOutputRows
中调用变量分配器,因为它会导致变量锁定。
我也 运行 解决了这个问题,但我的解决方案有点不同——将变量赋值移动到 PreExecute()
没有帮助。
相反,我所做的是我想解析三个不同的文件,并使用脚本组件读取每个文件。他们的专栏有点相似,所以我创建了一个数据流任务,确保它有效,然后复制它并修改每个副本以反映文件中的差异。 运行 每个单独的数据流任务都成功了,但是当我尝试 运行 其中两个时,一个接一个地循环,我在 HostPrimeOutput()
之后得到了一个 NullReferenceException
在我的脚本组件中调用 OutputBuffer.AddRow()
方法。
事实证明,当我复制每个数据流任务时,脚本组件都保持相同的命名空间,我猜它不喜欢这样。所以,我创建了 b运行d 个新脚本组件,再次设置所有输出列(呃!),将脚本主体复制过来,很高兴。