Search code examples
c#.netirepository

IRepository and Related Data?


a couple of weeks ago i just got a test for a job interview, the thing is that i've resolved almost everything except one point, that at this point, i guess it's impossible to resolve. but for sure i'm wrong.

This problem deals with related data. Our server API, IRepository, will only return a node with its immediate children (for example imagine that FakeRepository is loading from a Database. You must not change the code in FakeRepository for this test).

Imagine that the NodeManager is client side code.

We want to make sure that there is only ever one instance of a specific object on the client, so that our client has a consistent view of data in our application. The test “LoadingNodeB_ShouldAlwaysReturnTheSameInstance()” demonstrates this problem. Modify the code in the NodeManager so that this test passes.

so, we have this IRepo

namespace TestRecruitment.Repository
{
    public interface IRepository
    {
        Node GetNodeAndImmediateChildren(string nodeName);
    }
}  

and in a test project we have a fakerepository that loads the information.

using System;

using TestRecruitment.Repository;

namespace TestRecruitment.Test.Repository
{
    public class FakeRepository : IRepository
    {
        /// <summary>
        /// YOU MUST NOT CHANGE THIS METHOD
        /// </summary>
        public Node GetNodeAndImmediateChildren(string nodeName)
        {
            switch (nodeName)
            {
                case "Node A":
                    // do not modify this
                    var nodeB = new Node { Name = "Node B" };
                    var nodeC = new Node { Name = "Node C" };
                    return new Node { Name = "Node A", ImmediateChildren = new[] { nodeB, nodeC } };
                case "Node B":
                    // do not modify this
                    var nodeD = new Node { Name = "Node D" };
                    var nodeE = new Node { Name = "Node E" };
                    return new Node { Name = "Node B", ImmediateChildren = new[] { nodeD, nodeE } };
                case "Node E":
                    var nodeA = new Node { Name = "Node A" };
                    var nodeF = new Node { Name = "Node F" };
                    return new Node { Name = "Node E", ImmediateChildren = new[] { nodeA, nodeF } };
                default:
                    throw new ApplicationException("Unknown node");
            }
        }
    }
}

the thing is that you only can change the repository, you can't touch any other code.

using System.Linq;

namespace TestRecruitment.Repository
{
    public class NodeManager
    {
        private readonly IRepository _nodeRepository;

        public NodeManager(IRepository nodeRepository)
        {
            _nodeRepository = nodeRepository;
        }

        public Node GetNodeAndImmediateChildren(string name)
        {
            return _nodeRepository.GetNodeAndImmediateChildren(name);
        }
    }
}

well, that's it.

any help will be appreciated, i just want to know, how to resolve it.

i've created a github repo https://github.com/rothariger/TestRecruitment

Regards!


Solution

  • Assuming that a node is uniquely identified by its name, one way of making sure you return the same instance is to keep a local cache in the client and maintain the Node.ImmediateChildren collection every time a call is made to the repository:

    public class NodeManager
    {
        private readonly IRepository _nodeRepository;
    
        private readonly Dictionary<string, Node> _cachedNodes = new Dictionary<string, Node>();
    
        public NodeManager(IRepository nodeRepository)
        {
            _nodeRepository = nodeRepository;
        }
    
        public Node GetNodeAndImmediateChildren(string name)
        {
            var repositoryNode =_nodeRepository.GetNodeAndImmediateChildren(name);
            UpdateCache(repositoryNode);
            return _cachedNodes[repositoryNode.Name];
        }
    
        private void UpdateCache(Node repositoryNode)
        {
            AddOrUpdateChildrenCollectionInCache(repositoryNode);
    
            foreach (var childNode in repositoryNode.ImmediateChildren)
            {
                AddOrUpdateChildrenCollectionInCache(childNode);
            }
        }
    
        private void AddOrUpdateChildrenCollectionInCache(Node repoNode)
        {
            if (_cachedNodes.TryGetValue(repoNode.Name, out var cachedNode))
            {
                cachedNode.ImmediateChildren = repoNode.ImmediateChildren;
            }
            else
            {
                _cachedNodes.Add(repoNode.Name, repoNode);
            }
        }
    }