0

WP7/8 アプリで、ユーザーから次のエラー メッセージが表示されることがあります (ここでは問題を再現できません)。

[Type]:[ArgumentException]
[ExceptionMessage]:[Value does not fall within the expected range.]
[StackTrace]:[
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(Date key, WorkDay value, Boolean add)
   at MyProject.Core.Data.WorkDayCache.GetWorkDay(Date date, Boolean returnCorrected)
   at MyProject.Core.Calculations.CalculationHelper.WorkTimeDay(Date date)
   at MyProject.WP.UI.InfoBoxes.IBWorkTimeToday.UpdateMinute()
   at MyProject.WP.UI.InfoBoxes.IBWorkTimeToday.Update()
   at MyProject.WP.UI.MainPage.UpdateInfoBoxes()
   at MyProject.WP.UI.MainPage.ButtonStart_Click(Object sender, RoutedEventArgs e)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
   at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
   at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
]
[InnerException]:[none]

GetWorkDay メソッドのコードは次のとおりです。

/// <summary>
/// Returns the cached work day for a given date
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public WorkDay GetWorkDay(Date date, bool returnCorrected = true)
{
    // return the cached value in case it is cached and the filter is disabled
    if (!TagFilter.IsFilterEnabled)
    {
        if (_cachedWorkDays.ContainsKey(date))
        {
            if (returnCorrected)
            {
                var correctedWorkDay = new TimeCalculatorInterface().GetCorrectedWorkDay(_cachedWorkDays[date]);
                return correctedWorkDay;
            }
            return _cachedWorkDays[date];
        }
    }

    // nothing cached, thus get the result and cache it
    var workDays = _databaseController.Wait().GetWorkDays(date, date, false);
    if (workDays != null && workDays.Count > 0)
    {
        if (!TagFilter.IsFilterEnabled)
            _cachedWorkDays.Add(date, workDays[0]);

        // correct the work day times with the break times if enabled
        if (returnCorrected)
        {
            var correctedWorkDay = new TimeCalculatorInterface().GetCorrectedWorkDay(workDays[0]);
            return correctedWorkDay;
        }

        return workDays[0];
    }

    return new WorkDay();
}

私の主な問題は、例外の原因が理解できないことです。この 2 日間、このメッセージは、キーが既に存在するディクショナリにキーと値のペアを追加しようとしていることを意味しているだけだという印象を受けていました。ただし、このキャッシュはキーが既に存在するかどうかを直前にチェックし、この場合はキャッシュされた値を返します。何千もの挿入を含む詳細な単体テストをいくつか作成しましたが、何も起こりませんでした。

スタック トレースで奇妙なのは、GetWorkDay() の直後に Dictionary2.Insert() が呼び出されていることです。しかし、重複キーの問題があることがわかったすべてのスタック トレースは、前に Dictionary2.Add() を呼び出します (Insert() を直接呼び出すことができないため、コードで実際に行います。

この例外をスローする可能性のあるものはありますか?

さらに知っておくべきこと:

_cachedWorkDays は、キー タイプ Date と値タイプ WorkDay を持つ唯一のディクショナリです。

Date は、日付の私自身の実装です (DateTime が提供するよりも日付を操作するためのメソッドがいくつか必要でした。さらに、DateTime の時刻部分が日付処理に影響を与えないようにしたかったのです)。Date をディクショナリのキーとして使用するため、次のように Equals と GetHashCode のオーバーライドが必要です)

public static bool operator ==(Date d1, Date d2)
{
    return d1.Day == d2.Day && d1.Month == d2.Month && d1.Year == d2.Year;
}   

public override bool Equals(object obj)
{
    if (obj.GetType() == this.GetType())
    {
        Date obj1 = (Date)obj;
        return obj1 == this;
    }
    return false;
}

public override int GetHashCode()
{
    return (Year*100 + Month)*100 + Day;
}

どんな助けでも大歓迎です。

よろしく、ステファン

4

1 に答える 1

1

考えられる理由 #1: 同時実行の問題。スレッド A が実行されている間_databaseController.Wait()、スレッド B はその日をキャッシュに追加しました。スレッド A が起動した後、まったく同じ例外が表示されます。

考えられる理由 #2: Date クラスが変更可能です。変更可能なデータ型をキーとして使用すると、辞書が完全に台無しになります。System.DateTime(a) immutable (b) struct (c) implements であることに注意してくださいIEquatable<DateTime>

PS APIを使用すると、以降dict.TryGetValue(..)よりも効率的ですdict.ContainsKey(..)dict[..]

于 2013-09-13T02:05:36.357 に答える