0

好的,所以我在这行代码中偶尔会收到 NullReferenceException:

if (!_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key) || _oracleTenantSettings.OraclePlanSettings[_key] == null)

和/或这一行:

_oraclePlanSettings = _oracleTenantSettings.OraclePlanSettings[_key];

其中 OraclePlanSettings 是一个 SortedList,它不能为空,因为有问题的代码由以下内容包围:

 if (_oracleTenantSettings.OraclePlanSettings != null && _oracleTenantSettings.OraclePlanSettings.Count > 0)

所以我得到了一个 NRE,但整个代码行中没有一个部分可能永远为空。时期。(感觉到挫败感?)这包括密钥,但无论如何都不会抛出 NRE。我不明白。VS可能只是放错了CLR异常吗?如果是这样,哪里是开始寻找的好地方?

堆栈跟踪只是一个单行:

 at company.product.Mvc.OracleSettingsStoreCache.VerifyValueInCacheOrInsert[T](T& returnVal, SettingsType settingType, String tenantId, String planId, String pageMnemonic, String processId, String transcationType, String language, String country, String wapTransactionType, String wapCodeGroup, String wapLoanReasons, String palleteType, Boolean isInsert, Object _cacheValue) in blahblahblah.OracleSettingsStoreCache.cs:line 290

这是整个代码块:

if (!string.IsNullOrEmpty(tenantId) && (!IsWacMode() || (IsWacMode() && settingType == OracleSettingsType.SettingsType.FetchWAPInvestmentTransfer)) && _useCache != "false")
                {
                    tenantId = tenantId.ToUpper().Trim();

                    _oracleTenantSettings = null;

                    if (_oracleCacheManager.Contains(_cacheKey))
                        _oracleTenantSettings = _oracleCacheManager.Get<OracleTenantSetting>(_cacheKey);

                    if (_oracleTenantSettings != null)
                    {
                        if (_oracleTenantSettings.OraclePlanSettings != null && _oracleTenantSettings.OraclePlanSettings.Count > 0)
                        {
                            _key = language + "_" + country + "_" + tenantId;
           ***LINE 290***   if (!_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key) || _oracleTenantSettings.OraclePlanSettings[_key] == null)
                            {
                                _objectMissing = TypeOfObjectMissing.TenantObjectDoesNotExist;
                            }
                        }
4

4 回答 4

4

如果没有看到代码所在的上下文,就很难确定。但是根据您描述的症状,即非常零星的......莫名其妙的......不可能的东西是空的......我强烈怀疑是线程问题。例如,如果集合是静态的并且可能被多个线程访问,则可能会发生(尽管这是一种罕见的机会时机)第二个线程在第一个线程测试是否存在某些东西和它访问之间修改集合内容那东西。

如果是这种情况,您必须使您的代码更加线程安全。您可以使用锁定或并发集合来避免此问题。要使用锁,您需要使用同步对象(而不是动态创建的新对象)。您还希望搜索访问该集合的所有位置,并用锁围绕每个位置...仅查看该集合的代码必须使用锁以及修改该集合的代码。对于 SO 答案来说,这是一个很大的话题,所以我建议您使用这个非常棒的资源:

http://www.albahari.com/threading/

在这种情况下,您可以通过以下方式获得 NRE:

thread 1 checks if entry exists in SortedList myList for _key="hello" 
gets true
thread 1 checks if entry for _key="hello" is non-null
gets true
thread 2 sets myList["hello"] = null
thread 1 executes myList["hello"].Something() and gets NRE.

根据对您帖子的编辑,似乎在这些行中

if (_oracleTenantSettings != null) {
    if (_oracleTenantSettings.OraclePlanSettings != null && _oracleTenantSettings.OraclePlanSettings.Count > 0) {
        _key = language + "_" + country + "_" + tenantId;
         if (!_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key) || _oracleTenantSettings.OraclePlanSettings[_key] == null)

如果 NRE 发生在最后一行,则在执行第一行或第二行之后,另一个线程可以设置_oracleTenantSettings_oracleTenantSettings.OraclePlanSettings为 null。发生这些事情中的任何一个都会导致最后一行抛出 NRE。

以下代码不是使代码线程安全的正确方法,但可以作为一种快速方法来查看是否确实如此,因为它会降低这种情况(空引用异常)的可能性:

var oracleTS = _oracleTenantSettings;
if (oracleTS != null) {
    var planSettings = oracleTS.OraclePlanSettings;
    if ((planSettings != null) && (planSettings.Count > 0)) {
        _key = language + "_" + country + "_" + tenantId;
        if (!planSettings.ContainsKey(_key) || planSettings[_key] == null)

请注意,最后一行可能仍然存在与线程相关的其他问题,例如在条件的第一部分和第二部分之间被另一个线程删除的键,或者在测试后 planSettings Count 发生变化。但是,如果这段代码大大减少了 NRE,那么您就可以很好地了解发生了什么,并且您应该仔细检查并适当地使您的代码线程安全,并在需要的地方使用锁。进一步说,人们需要更多地了解其他代码在做什么,尤其是修改 _oracleTenantSettings 的代码。

于 2012-06-13T20:07:30.243 回答
3

我的猜测是有另一个线程访问该属性。

每次您像这样访问它时,一种快速修复它的方法是锁定它:

var oraclePlanSettings = _oracleTenantSettings.OraclePlanSettings;
lock (oraclePlanSettings)
{
    // from now on you can safely access your cached reference "oraclePlanSettings"
    if (oraclePlanSettings != null && oraclePlanSettings.Count > 0)
        _oraclePlanSettings = oraclePlanSettings[_key]; // ... blabla
}

谨防死锁。

于 2012-06-13T20:14:45.260 回答
1

我同意以前的答案,这可能是线程问题,但我有一些要补充的。

这是一个快速而肮脏的测试,以确定它是否是线程。

设置一个重现错误的场景(比如在一夜之间在几个(10)个线程中以恒定循环运行它)

将此属性应用于您的班级

[同步​​]

您的类必须继承自 ContextBoundObject。

这会强制类的所有实例在单个线程上运行(速度较慢)。

重新运行您的测试。

如果您的问题消失了,则说明您遇到了线程问题。如果速度是一个问题,您需要返回并围绕接触该对象的代码进行所有锁定。如果您将所有内容转换为使用相关对象的属性,您只需锁定 getter 和 setter。

如果快速而肮脏的测试无法解决问题,则可能是其他问题。例如,如果您使用不安全的代码或不安全的 dll(即用非 .Net c++ 编写的东西),则可能是内存损坏问题。

希望这可以帮助。

这里是关于属性的更多细节,包括从 ContextBoundObject 继承。

女士文档

代码示例:

// Context-bound type with the Synchronization context attribute.
[Synchronization()]
public class SampleSynchronized : ContextBoundObject {

    // A method that does some work, and returns the square of the given number.
    public int Square(int i)  {

        Console.Write("The hash of the thread executing ");
        Console.WriteLine("SampleSynchronized.Square is: {0}", 
                             Thread.CurrentThread.GetHashCode());
        return i*i;
    }
}
于 2012-06-14T14:31:50.877 回答
-1

更新

我建议在代码中的某处 Equality 运算符或 == 已在相关对象之一上重载,并且当未正确检查 null (或失败)或返回相等时发生故障不是。

检查在这种情况下使用的任何类对象的 == 上的所有运算符重载并纠正..

原来的

将逻辑更改为此,因为您想首先检查是否没有键...然后..当存在有效键但()其值为空时进行检查:

 if ((_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key) == false) || 
     ((_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key)) && 
       _oracleTenantSettings.OraclePlanSettings[_key] == null)))

如果您仔细考虑原始语句的逻辑流程,它实际上是预期的,为什么它会间歇性地失败。:-)

编辑:让我解释一下,按步骤遵循这个逻辑

  1. 在原始 if 子句中,当它评估 (!ContainsKey(_key)) 时,意味着当密钥不存在时 (true),它会更改为 FALSE。
  2. 然后由于 #1 中的 False,Or 开始起作用。它评估 OraclePlanSettings[_key]关键不在那里吗?
  3. 因此,它执行代码以检查 null 是否存在无效键并引发异常。

只有打破我所展示的逻辑,才不会抛出异常。

于 2012-06-13T20:38:39.187 回答