繁体   English   中英

自定义聚合函数间歇性地引发NullReferenceException

[英]Custom aggregate function intermittently throws NullReferenceException

我有一个SQL CLR聚合函数,对同一个数据集执行该函数时会偶尔抛出System.NullReferenceException异常。 自定义聚合的目的是:

返回last(x,y) ,其中xDATETIME列, yINTEGER列。

将返回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更有可能错了!)。 简而言之,我在这里想念的是什么?

澄清:

  • 我相信我已经满足了SqlUserDefinedAggregate合同的要求(已实现所有必需的方法)
  • 该代码初始化Init方法中的所有成员变量(也是合同实现的一部分),以确保如果SQL重用(允许的话)另一个组的聚合实例,则将其清除且不为空
  • 显然,我错过了预期会遇到的细微差别,因为我看不到抛出NullReferenceException的原因。 我错过了什么?

可能不是答案,但是由于我需要包含代码,因此我不会将其发布为注释。

为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方法无关。

  • 为了澄清一些有关在.IsNulltrueSqlDateTime如何处理比较运算符(即<> )的问题:如果这些运算符的任何一方具有.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM