1

I am trying to do something that seems like it should be easy, but it is not working. I have a dictionary objects with an int key. Within the objects, I have a property, PositionInEvent, that I want to match the key of the dictionary within it. It seems like this should be a simple loop operation, but it's not working. Here's what I have:

private void ensurePositions(ref Dictionary<int, DisplayUnit>  dict)
{
    var keys = dict.Keys.ToArray();
    foreach(var key in keys)
    {
        dict[key].PositionInEvent = key;
    }
}

When I run this on a dictionary of 5 objects with keys of 0-4 (it won't always be sequential like this, but I'm unit testing it), the PositionInEvent property on each item in the event has a value of 4. Every single one. Why???? How can I do what I'm trying to do. It seems like it should be simple.

Update:

It was requested that I show how DisplayUnit is declared, instantiated, and added to the dictionary.

Here's the class declaration (I've taken out the stuff unrelated to instantiation and the property I'm working with here):

/// <summary>
/// This is the base display unit from which all other units are derived.
/// </summary>
public abstract class DisplayUnit
{

    /// <summary>
    /// Initializes a new instance of the <see cref="AbstractClasses.DisplayUnit"/> class.
    /// </summary>
    protected DisplayUnit (Dictionary<string,string> attributes)
    {
        this.Id = Guid.NewGuid();
        tryApplyAttributes(attributes);
    }

    protected DisplayUnit(Guid id, Dictionary<string,string> attributes)
    {
        this.Id = id;
        tryApplyAttributes(attributes);
    }

    private void tryApplyAttributes(Dictionary<string,string> attributes)
    {
        string name;
        attributes.TryGetValue("Name", out name);
        Name = name;

        string description;
        attributes.TryGetValue("Description", out description);
        Description = description;

        string dateTime;
        attributes.TryGetValue ("DateCreated", out dateTime);
        DateTime date;
        DateTime.TryParse(dateTime,out date);
        DateCreated = date;

        string guid;
        attributes.TryGetValue("AssociatedEvent", out guid);
        Guid id;
        Guid.TryParse(guid, out id);
        AssociatedEvent = id;

        string group;
        attributes.TryGetValue("GroupId", out group);
        Guid groupId;
        var groupSet = Guid.TryParse(group, out groupId);

        string posInGroup;
        attributes.TryGetValue("PositionInGroup", out posInGroup);
        int intPos;
        var posSet = int.TryParse(posInGroup, out intPos);

        if (posSet && groupSet)
            UnitGroup = new DisplayUnitGrouping (intPos, groupId);

        string pos;
        attributes.TryGetValue("PositionInEvent", out pos);
        int position;
        int.TryParse (pos, out position);
        PositionInEvent = position;
    }

    public Guid Id {
        get;
        private set;
    }

    private int _positionInEvent;
    public int PositionInEvent {
        get{
            return _positionInEvent;
        }
        set {
            if (value < 0) {
                throw new NegativePositionException ("Position of DisplayUnit must be positive.");
            }
            _positionInEvent = value;
        }
    }

}

TextUnit is the class I'm actually using, which derives from DisplayUnit:

public class TextUnit : DisplayUnit
{
    public string Text {
        get;
        set;
    }

    public TextUnit (Dictionary<string, string> attributes) : base (attributes)
    {
        SetAttributes (attributes);
        Plugin = new FaithEngage.Plugins.DisplayUnits.TextUnitPlugin.TextUnitPlugin ();
    }


    public TextUnit (Guid id, Dictionary<string, string> attributes) : base (id, attributes)
    {
        SetAttributes (attributes);
    }


    #region implemented abstract members of DisplayUnit

    public override void SetAttributes (Dictionary<string, string> attributes)
    {
        string text;
        attributes.TryGetValue ("text", out text);
        Text = text;
    }

    #endregion
}

The dictionary being acted upon comes from here. _duRepo is a faked repository (see code below this).

public Dictionary<int, DisplayUnit> GetByEvent(Guid eventId)
{
    try {
        var returnDict = new Dictionary<int,DisplayUnit>();
        var dict = _duRepo.GetByEvent(eventId);
     if (dict == null)
            return null;
        foreach(var key in dict.Keys)
        {
            var du = _factory.ConvertFromDto(dict [key]);
            if(du == null) continue;
            returnDict.Add (key, du);
        }
        ensurePositions(ref returnDict);
        return returnDict;
    } catch (RepositoryException ex) {
        throw new RepositoryException ("There was a problem accessing the DisplayUnitRepository", ex);
    }
 }

This all comes from this unit test (which I can't get to pass, and I don't know why):

[Test]
public void GetByEvent_ValidEventId_ReturnsDictOfEvents()
{
    var dict = new Dictionary<int,DisplayUnitDTO>();
    for(var i = 0; i < 5; i++)
    {
        dict.Add(i, new DisplayUnitDTO());
    }
    var repo = A.Fake<IDisplayUnitsRepository>();
    A.CallTo(() => repo.GetByEvent(VALID_GUID)).Returns(dict);
    A.CallTo(() => _fctry.ConvertFromDto(null))
        .WithAnyArguments()
        .Returns(
            new TextUnit(
                new Dictionary<string,string>(){
                    { "Text", "This is my Text" }
                }
            )
        );
    A.CallTo (() => _container.Resolve<IDisplayUnitsRepository>()).Returns(repo);
    var mgr = new DisplayUnitsRepoManager(_container);
    var duDict = mgr.GetByEvent(VALID_GUID);
    Assert.That(duDict, Is.InstanceOf(typeof(Dictionary<int,DisplayUnit>)));
    Assert.That(duDict, Is.Not.Null);
    Assert.That(duDict.Count == 5);
    foreach(var key in duDict.Keys)
    {
        Assert.That(duDict[key].PositionInEvent == key);
    }
}
Jon Falkenstein
  • 135
  • 1
  • 9
  • 4
    How are you creating the Display unit object. It looks like all four items in the dictionary is referencing the same DusplayUnit instance. – ShuberFu May 15 '16 at 14:52
  • Can you show the declaration of the `DisplayUnit` too please? btw: the `ref` keyword seems unnecessary since you're only changing the content of that dictionary, not the reference itself, but that should not be the error. – René Vogt May 15 '16 at 14:52
  • 1
    I created a simple JS Fiddle for this: https://dotnetfiddle.net/T1Z9PW. I'd agree with Sterling W that your construction of the Dictionary is suspect. – syazdani May 15 '16 at 14:56
  • [Minimal, complete, and verifiable example](http://stackoverflow.com/help/mcve) would really help – Fabjan May 15 '16 at 15:00
  • Seems that the values of the dictionary are all added by reference, hence all the items took the value 4. – Sufyan Jabr May 15 '16 at 21:06

1 Answers1

2

So the comments were instructive here. Based upon those, I realized the direction I needed to look. The culprit here was the line:

 A.CallTo(() => _fctry.ConvertFromDto(null))
    .WithAnyArguments()
    .Returns(
        new TextUnit(
            new Dictionary<string,string>(){
                { "Text", "This is my Text" }
            }
        )
    );

Essentially, this had to do with FakeItEasy and the way it fakes return values. Even though I had newed up a TextUnit in the return value, FakeItEasy took that new object and returned a reference to it ever single time that _fctry.ConvertFromDto() was called. Thus my fake was giving me strange behavior that otherwise wouldn't have been happening (I wouldn't ever add the same item multiple times to a dictionary by reference).

Anyway, I was able to fix this by changing my return specification:

A.CallTo (() => _fctry.ConvertFromDto (null))
    .WithAnyArguments()
    .ReturnsLazily((DisplayUnitDTO d) => new TextUnit(d.Attributes));

After I tested this out, this creates a new text unit every time the function is called. (btw... I know I didn't actually use d in the lambda, but I needed to fake the return value using the same signature the call would be made with.)

Thanks for commenters and their pointers. I've renamed this question to better relate to what was actually happening.

Jon Falkenstein
  • 135
  • 1
  • 9
  • The crux of the matter is "when are function arguments are evaluated?": before a function is called. The original `Returns(new TextUnit(…))` has to create the `TextUnit` first, so there can only be one value used. It's the same as if you'd written `var theTextUnit = new TextUnit(…); A.CallTo(…).Returns(theTextUnit);`. In that case, you wouldn't have been surprised if the same `TextUnit` were used each time. – Blair Conrad May 17 '16 at 00:09