Frage

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

War es hilfreich?

Lösung

Possible reason #1: concurrency issue. While thread A is executing _databaseController.Wait(), thread B has added that day to the cache, after the thread A wakes up you'll see exactly the same exception.

Possible reason #2: your Date class is mutable. Using mutable data types as keys will completely screw the dictionary. Please note System.DateTime is (a) immutable (b) struct (c) implements IEquatable<DateTime>.

P.S. Using dict.TryGetValue(..) API is more efficient than dict.ContainsKey(..) and subsequent dict[..]

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top