简体   繁体   English

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

[英]Custom aggregate function intermittently throws NullReferenceException

I have a SQL CLR aggregate function which sporadically throws a System.NullReferenceException exception when executed against the same dataset. 我有一个SQL CLR聚合函数,对同一个数据集执行该函数时会偶尔抛出System.NullReferenceException异常。 The purpose of the custom aggregate is to: 自定义聚合的目的是:

Return latest(x, y) where x is a DATETIME column and y is an INTEGER column. 返回last(x,y) ,其中xDATETIME列, yINTEGER列。

The value for column y for the most recent value of column x will be returned. 将返回x列的最新值的y列的值。

The dataset being hit by the query is a subset of 142,145 rows of a 2,931,563 row table, with the aggregation resulting in (when it runs) 141,654 rows being returned. 被查询命中的数据集是2,931,563行表的142,145行的子集,聚合导致(运行时)返回141,654行。

The code for the CLR aggregate function is as follows: 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;
    }
};

As far as I can tell there's nowhere in the function that can result in a null reference, assuming that SQL server is following the contract outlined on MSDN (though it's much more likely I'm wrong than SQL Server!). 据我所知,假设SQL Server遵循MSDN上概述合同 ,该函数中没有任何地方可以导致null引用(尽管我比SQL Server更有可能错了!)。 So in short, what am I missing here? 简而言之,我在这里想念的是什么?

To clarify: 澄清:

  • I believe I've met the requirements of the contract for a SqlUserDefinedAggregate (all required methods implemented) 我相信我已经满足了SqlUserDefinedAggregate合同的要求(已实现所有必需的方法)
  • The code initialises all member variables in the Init method (again part of the contract implementation) to ensure that if SQL re-uses (as it's permitted to) an instance of the aggregate for a different group it's cleaned down and non-null 该代码初始化Init方法中的所有成员变量(也是合同实现的一部分),以确保如果SQL重用(允许的话)另一个组的聚合实例,则将其清除且不为空
  • Clearly I've missed a nuance of the contract that I'm expected to meet as I can see no reason for the NullReferenceException to be thrown. 显然,我错过了预期会遇到的细微差别,因为我看不到抛出NullReferenceException的原因。 What have I missed? 我错过了什么?

This may turn out not to be the answer, but as I need to include code, I won't post it as a comment. 可能不是答案,但是由于我需要包含代码,因此我不会将其发布为注释。

Having a NULL value for value.latestDateTime wouldn't cause this error. 为value.latestDateTime使用NULL值不会导致此错误。 You only get the NULLReferenceException when an OBJECT is null, and you try to refer to it (by accessing it's properties for instance). 仅当OBJECT为null时,您才获得NULLReferenceException ,并尝试引用它(例如,通过访问它的属性)。

The only place in your code where I see you referencing an object is in the Merge void. 在代码中,我看到您引用对象的唯一位置是Merge void。 The object is value which is of type Latest . 该对象是类型为Latest value

I know you say it's not theoretically possible for value to ever be null, but that can't be proven or disproven in the code you've posted since Merge is public and therefore accessed from other applications. 我知道您说过,从理论上讲,值永远不可能为空,但是由于Merge是公开的,因此可以从其他应用程序访问,因此在您发布的代码中无法证明或证明这一点。

I have found in OOP that it's safest to ALWAYS null-check any object before referencing it. 我在OOP中发现,最安全的方法是始终在引用对象之前对所有对象进行空检查。 All you have to do is change your Merge code to this (if my c# memory serves...if my syntax is off, I'm sure you'll get the idea): 您所要做的就是将您的Merge代码更改为此(如果我的c#内存可用...如果我的语法关闭,那么我确定您会明白的):

public void Merge(Latest value)
{
    if (value != null)
    {
        if ((value.latestDateTime < latestDateTime) || (latestDateTime.IsNull))
        {
            latestValue = value.latestValue;
            latestDateTime = value.latestDateTime;
        }
    }
}

It's up to you if you want to do something else when value IS null. 如果您想在值IS为null时执行其他操作,则取决于您。 But this makes this stretch of code absolutely safe from NullReferenceExceptions, rather than trusting to "theoretically shouldn't be possible", which almost always means "IS possible" ; 但这使得这段代码绝对不受NullReferenceExceptions的影响,而不是信任“理论上不可能”,这几乎总是意味着“可能”; )

Everything does appear to be correct. 一切似乎都是正确的。 Except for possibly one thing. 除了可能的一件事。 Can either of the source columns for the two input parameters be NULL ? 两个输入参数的任何一个源列都可以为NULL吗? The code itself does seem to handle NULL s. 代码本身似乎确实可以处理NULL However, if there are NULL s in the data then I think you have the IsInvariantToNulls property of the SqlUserDefinedAggregate attribute set incorrectly as it should be false since a NULL in either field could affect the result. 但是,如果数据中有NULL ,那么我认为您的IsInvariantToNulls属性的SqlUserDefinedAggregate属性设置不正确,因为它应该为false因为任一字段中的NULL都可能影响结果。

Also: 也:

  • The datatype of the field being used for recordDateTime is DATETIME and not DATETIME2 , correct? 用于recordDateTime的字段的数据类型是DATETIME而不是DATETIME2 ,对吗?

  • Try running the query after adding OPTION(MAXDOP 1) as a query hint as that should prevent the Merge method from being called and that can help narrow down the issue. 在添加OPTION(MAXDOP 1)作为查询提示之后,请尝试运行查询,因为这应该可以防止调用Merge方法,并且可以帮助缩小问题的范围。 If the exception never happens with OPTION(MAXDOP 1) then it most likely has to do with the Merge method. 如果OPTION(MAXDOP 1)永远不会发生异常,则它很可能与Merge方法有关。 But if it still occurs, then it most likely has nothing to do with the Merge method. 但是,如果仍然发生,则很可能与Merge方法无关。

  • To clarify some questioning of how SqlDateTime handles comparison operators (ie the < and > ) when .IsNull is true : it is handled properly and will return a false if either side of those operators has .IsNull == true . 为了澄清一些有关在.IsNulltrueSqlDateTime如何处理比较运算符(即<> )的问题:如果这些运算符的任何一方具有.IsNull == true它将被正确处理并返回false

  • I would also remove the [StructLayout(LayoutKind.Sequential)] decorator. 我还将删除[StructLayout(LayoutKind.Sequential)]装饰器。 The default .NET handling should be fine. 默认的.NET处理应该很好。

I would add explicit check for NULL: 我将为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;
            }
        }
    }
}

Besides, your logic in Merge seems wrong... I'd repeat the same code as in Accumulate : 此外,您在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.

相关问题 XmlCompiledTransform.Load间歇性地引发NullReferenceException - XmlCompiledTransform.Load throws a NullReferenceException intermittently Xamarin自定义UITableViewCell抛出System NullReferenceException - Xamarin custom UITableViewCell throws System NullReferenceException iTunesMobileDevice.dll的自定义实现将引发NullReferenceException - custom implementation of iTunesMobileDevice.dll throws NullReferenceException 自定义WCF通道在ChannelBase.EndInvoke上引发NullReferenceException - Custom WCF channel throws NullReferenceException on ChannelBase.EndInvoke 尝试从另一个脚本调用 function 会引发 NullReferenceException 错误 - Trying to call a function from another script throws the NullReferenceException error foreach在ObservableCollection上引发NullReferenceException - foreach throws NullReferenceException on ObservableCollection 调用谓词将引发NullReferenceException - Invoking a predicate throws NullReferenceException ImageSourceConverter抛出NullReferenceException ...为什么? - ImageSourceConverter throws a NullReferenceException … why? 对象抛出NullReferenceException - Object throws NullReferenceException 未设置DependencyProperty引发NullReferenceException - Unset DependencyProperty throws NullReferenceException
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM