[英]XmlCompiledTransform.Load throws a NullReferenceException intermittently
[英]Custom aggregate function intermittently throws NullReferenceException
我有一个SQL CLR聚合函数,对同一个数据集执行该函数时会偶尔抛出System.NullReferenceException
异常。 自定义聚合的目的是:
返回last(x,y) ,其中x是
DATETIME
列, y是INTEGER
列。将返回
x
列的最新值的y列的值。
被查询命中的数据集是2,931,563行表的142,145行的子集,聚合导致(运行时)返回141,654行。
CLR聚合函数的代码如下:
using System.Data.SqlTypes;
using System.Runtime.InteropServices;
using Microsoft.SqlServer.Server;
[StructLayout(LayoutKind.Sequential)]
[SqlUserDefinedAggregate(Format.Native, IsInvariantToDuplicates = true, IsInvariantToNulls = true, IsInvariantToOrder = true, IsNullIfEmpty = true, Name = "Latest")]
public class Latest
{
private SqlDateTime latestDateTime;
private SqlInt32 latestValue;
public void Init()
{
latestDateTime = SqlDateTime.Null;
latestValue = SqlInt32.Null;
}
public void Accumulate(SqlDateTime recordDateTime, SqlInt32 value)
{
if (latestDateTime.IsNull)
{
latestDateTime = recordDateTime;
latestValue = value;
}
else
{
if (recordDateTime > latestDateTime)
{
latestDateTime = recordDateTime;
latestValue = value;
}
}
}
public void Merge(Latest value)
{
if ((value.latestDateTime < latestDateTime) || (latestDateTime.IsNull))
{
latestValue = value.latestValue;
latestDateTime = value.latestDateTime;
}
}
public SqlInt32 Terminate()
{
return latestValue;
}
};
据我所知,假设SQL Server遵循MSDN上概述的合同 ,该函数中没有任何地方可以导致null引用(尽管我比SQL Server更有可能错了!)。 简而言之,我在这里想念的是什么?
澄清:
Init
方法中的所有成员变量(也是合同实现的一部分),以确保如果SQL重用(允许的话)另一个组的聚合实例,则将其清除且不为空 可能不是答案,但是由于我需要包含代码,因此我不会将其发布为注释。
为value.latestDateTime使用NULL值不会导致此错误。 仅当OBJECT为null时,您才获得NULLReferenceException
,并尝试引用它(例如,通过访问它的属性)。
在代码中,我看到您引用对象的唯一位置是Merge void。 该对象是类型为Latest
value
。
我知道您说过,从理论上讲,值永远不可能为空,但是由于Merge是公开的,因此可以从其他应用程序访问,因此在您发布的代码中无法证明或证明这一点。
我在OOP中发现,最安全的方法是始终在引用对象之前对所有对象进行空检查。 您所要做的就是将您的Merge代码更改为此(如果我的c#内存可用...如果我的语法关闭,那么我确定您会明白的):
public void Merge(Latest value)
{
if (value != null)
{
if ((value.latestDateTime < latestDateTime) || (latestDateTime.IsNull))
{
latestValue = value.latestValue;
latestDateTime = value.latestDateTime;
}
}
}
如果您想在值IS为null时执行其他操作,则取决于您。 但这使得这段代码绝对不受NullReferenceExceptions的影响,而不是信任“理论上不可能”,这几乎总是意味着“可能”; )
一切似乎都是正确的。 除了可能的一件事。 两个输入参数的任何一个源列都可以为NULL
吗? 代码本身似乎确实可以处理NULL
。 但是,如果数据中有NULL
,那么我认为您的IsInvariantToNulls
属性的SqlUserDefinedAggregate
属性设置不正确,因为它应该为false
因为任一字段中的NULL
都可能影响结果。
也:
用于recordDateTime
的字段的数据类型是DATETIME
而不是DATETIME2
,对吗?
在添加OPTION(MAXDOP 1)
作为查询提示之后,请尝试运行查询,因为这应该可以防止调用Merge
方法,并且可以帮助缩小问题的范围。 如果OPTION(MAXDOP 1)
永远不会发生异常,则它很可能与Merge
方法有关。 但是,如果仍然发生,则很可能与Merge
方法无关。
为了澄清一些有关在.IsNull
为true
时SqlDateTime
如何处理比较运算符(即<
和>
)的问题:如果这些运算符的任何一方具有.IsNull == true
它将被正确处理并返回false
。
我还将删除[StructLayout(LayoutKind.Sequential)]
装饰器。 默认的.NET处理应该很好。
我将为NULL添加显式检查:
public void Accumulate(SqlDateTime recordDateTime, SqlInt32 value)
{
if (latestDateTime.IsNull)
{
latestDateTime = recordDateTime;
latestValue = value;
}
else
{
if (!recordDateTime.IsNull)
{
if (recordDateTime > latestDateTime)
{
latestDateTime = recordDateTime;
latestValue = value;
}
}
}
}
此外,您在Merge
的逻辑似乎是错误的...我将重复与Accumulate
相同的代码:
public void Merge(Latest value)
{
if (latestDateTime.IsNull)
{
latestDateTime = value.latestDateTime;
latestValue = value.latestValue;
}
else
{
if (!value.latestDateTime.IsNull)
{
if (value.latestDateTime > latestDateTime)
{
latestDateTime = value.latestDateTime;
latestValue = value.latestValue;
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.