views:

241

answers:

3

I have a datamapper class that will eager load any property that does not have a Lazy attribute attached to it. I have two entities, State and Country, Country has an inverse relationship to state in that it contains a list of all the states of that country and State has a forward relationship to Country in that it has a property Country that eager loads the country that it is assigned to. However if I try and retrieve one of these objects, lets say a State this is what happens:

  1. State is loaded by mapper
  2. Mapper reaches an eager property Country
  3. Mapper retrieves the country for that state
  4. Mapper loads the country
  5. Mapper reaches an eager collection property of States
  6. Mapper loads a list of states and begins mapping each individual one using the cache where it can.
  7. GOTO 1 for each state loaded in country

I am at a loss as to how I can avoid this loop. So mainly I am looking for ideas. I will post any code anyone asks, but this process encompasses A LOT of lines of code so I didn't want to flood the question with code.

Thanks in advance!

Edit:

Alright after following Matt Howells advice and investigating deeper into the datamapper pattern Martin Fowler does indeed speak about a cyclic reference on page 169 and 170. His suggestion is to use an empty object and load it into an identity map and return it thus stopping the recursive loading. I've read this paragraph about 1000 times now and I still don't understand how this stops the load and beyond that I am lost as to when or how I would know when to load this empty object into my identity map. I apologize for being dense here, but this just seems to be flying right over my head.

Thanks again.

+1  A: 

The datamapper should catch circular references. Is it a homegrown datamapper?

EricSchaefer
It is homegrown.....how would I implement some form of catch for this? That is where I am at a loss in adjusting my datamapper code to include a watch for this.
joshlrogers
Record the properties you already visited and compare the current property against that list.
EricSchaefer
So, fetch depth is typically implemented at the property level and not object level? So lets say my max fetch depth is 3, so the property could be mapped 3 times but the depth of the object graph could be significantly higher than that. Am I correct in that assumption?
joshlrogers
+2  A: 

Consider loading objects through a Repository that keeps track of which objects have been loaded.

Edit: If you are doing your own ORM (and even if you are not) I highly recommend Martin Fowler's book Patterns of Enterprise Application Architecture. I vaguely recall him talking about this loop situation in the book so it might help you.

Edit 2: At steps 4 and 5 of your loop if you have already loaded the country then there is no need to eagerly load its states because they should already be loaded. This breaks the infinite loop.

Matt Howells
I actually do keep track of all the objects that have been loaded. However, and I guess I am just being dense, I am unable to see a way in which I can utilize this. I am trying to control the fetch depth but it has to be controlled specific to an originator. What I am trying to achieve is essentially this: State(Originator) -> Country -> States -> [STOP] but when I map each individual State that is in States the entities have no concept of the existence of the originator of the request. So, it seems applying a counter that is particular to the originator seems to be out of the question. Yes?
joshlrogers
I have it on my desk at work, will see if I can find anything tomorrow. Thank you.
joshlrogers
I added Martin Fowlers advice, but I am still lost. Any advice?
joshlrogers
A: 

I just wanted to post the solution that I came up with, however I believe there are many ways to skin this cat.

Here is my FetchDepthCounterClass I created:

public static class FetchDepthCounter
{
    private static Dictionary<Type, int> _DepthCounter;
    private static int _MaxDepth = 3;

    static FetchDepthCounter()
    {   
        _DepthCounter = new Dictionary<Type, int>();
    }

    public static void SetDepth(int depth)
    {
        _MaxDepth = depth;
    }

    public static void ResetCounter()
    {
        _DepthCounter.Clear();
    }

    public static bool IncrementCounter(Type entityType)
    {
        if(!_DepthCounter.ContainsKey(entityType))
        {
            _DepthCounter.Add(entityType, 0);
            return true;
        }

        if(_DepthCounter[entityType] < _MaxDepth)
        {
            ++_DepthCounter[entityType];
            return true;
        }

        return false;
    }

}

IncrementCounter returns a bool stating whether the max fetch depth has been reached or not. I call increment counter as part of my mapping process right before I set the value of the property. First I determine that what I am having to load is another DTO object or a collection of DTOs and I pass the parent type and increment on that type. So this is the small little bit of code inside my SetValue method in my datamapper:

if(isDto)
{
    if (!FetchDepthCounter.IncrementCounter(property.ComponentType))
        return;
}

That is it, that seems to do it. All of my unit tests are passing. Thanks for everyone's help. I hope this helps someone later on. Once again, this probably would have been much easier wrapping it in a unit of work pattern, and eventually I may do that, but this gets the job done for now.

joshlrogers