0

in my WP7/8 app I get sometimes the following error message from my users (can't reproduce the issue here).

[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]

Here is the code of the GetWorkDay method:

/// <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();
}

My main issue is that I don't understand what causes the exception. I've been under the impression for the last two days that this message just means that it tries to add a keyvaluepair into the dictionary where the key already exists. But this cache checks right before whether the key already exists and returns the cached value in this case. I wrote a couple of detailed unit tests with thousands of inserts and nothing happened.

What's odd in the stack trace is the fact that right after GetWorkDay() Dictionary2.Insert() is called. But all the stack traces I found that have a duplicate key issue call the Dictionary2.Add() before (which I actually do in the code because I can't call Insert() directly.

So is there anything I miss which might throw this exception?

Some more things to know:

_cachedWorkDays is the only dictionary there with key type Date and value type WorkDay

Date is my own implementation of a date (Needed some more methods for working with dates than DateTime provided me. Additionally, I wanted to make sure the time part in DateTime doesn't influence my date processings). As I use Date as key in the dictionary it requires an Equals and GetHashCode override which is as follows)

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;
}

Any help is highly appreciated.

Regards, Stephan

4

1 回答 1

1

可能的原因 #1:并发问题。当线程 A 正在执行时_databaseController.Wait(),线程 B 已将这一天添加到缓存中,在线程 A 唤醒后,您将看到完全相同的异常。

可能的原因 #2:您的 Date 类是可变的。使用可变数据类型作为键将完全搞砸字典。请注意System.DateTime(a) 不可变 (b) 结构 (c) 实现IEquatable<DateTime>

PS 使用dict.TryGetValue(..)API 比dict.ContainsKey(..)后续更高效dict[..]

于 2013-09-13T02:05:36.357 回答