'Infinite Loop When Loading Object

I've been racking my brains for a few days, trying not to generate infinite loops when I load my classes with data coming from a database through a method inside the class.

In a playful example this is what is happening. Consider the class below:

    public class Person
{
    public int id { get; set; }
    public string name{ get; set; }
    public Person carrier{ get; set; }
}

In the method inside the class I defined it like this:

public void Load()
{
    Datatable tableResult;
    using (MySqlConnection con = new MySqlConnection(ConnectionString))
    {
        using (MySqlCommand com = new MySqlCommand(My.Resources.PersonSelect))
        {
            com.Parameters.AddWithValue("@ID", this.ID);
            using (MySqlDataAdapter adp = new MySqlDataAdapter(CmdPerson))
            {
                TableResult = new DataTable();
                adp.Fill(TableResult);
                if (tableResult.Rows.Count == 1)
                {
                    ID = tableResult.Rows(0).Item("id");
                    Name = tableResult.Rows(0).Item("name");
                    Carrier = new Person().Load(tableResult.Rows(0).Item("carrierid"));
                }
            }
        }
    }
}

Also consider that I have a person, and that person's carrier is himself. this would generate an infinite loop.

I've already tried implementing LazzyLoading in the properties, but I still don't know which classes might have this same problem. Is there any other way to solve this problem?



Solution 1:[1]

You can try this:

public void Load()
{
    var tableResult = new DataTable(); 
    using (var con = new MySqlConnection(ConnectionString))
    using (var com = new MySqlCommand(My.Resources.PersonSelect))
    using (var adp = new MySqlDataAdapter(com))
    {
        com.Parameters.AddWithValue("@ID", this.ID);
        adp.Fill(tableResult);
    }

    if (tableResult.Rows.Count == 1)
    {
        ID = tableResult.Rows(0).Item("id");
        Name = tableResult.Rows(0).Item("name");
        if (ID == tableResult.Rows(0).Item("carrierid"))
        {
            Carrier = this;
        }
        else 
        {
            Carrier = new Person().Load(tableResult.Rows(0).Item("carrierid"));
        }
    }
}

It still has potential to have a loop in a chain (Person A's carrier is Person B, who's carrier is Person A again), but if that's not a situation in your data you might be okay.

Otherwise you can opt to lazy-load the carrier, like this:

public class Person
{
    public int ID { get; set; }
    public string Name{ get; set; }

    private int _carrierID;
    private Person _carrier = null;
    public Person Carrier
    { 
       get 
       {
           if (_carrier == null) _carrier = Person.Load(_carrierID);
           return _carrier;                  
       }
       private set
       {
            _carrier = value;  
            if (_carrier != null) _carrierID = _carrier.ID;
       }
    }

    public static Person Load(int ID)
    {
        var tableResult = new DataTable();
        using (var con = new MySqlConnection(ConnectionString))
        using (var com = new MySqlCommand(My.Resources.PersonSelect))
        using (var adp = new MySqlDataAdapter(com))
        {
            com.Parameters.AddWithValue("@ID", ID);
            adp.Fill(tableResult);
        }

        if (tableResult.Rows.Count == 1)
        {
            return new Person() {
                ID = tableResult.Rows(0).Item("id"),
                Name = tableResult.Rows(0).Item("name"),
                _carrierid = tableResult.Rows(0).Item("carrierid")
            };
        }
        return null;
    }
}

This can also still possibly loop forever if you try to write code like this:

var p = Person.Load(someID);
while (p.Carrier!=null)
{
   p = p.Carrier;
}

But at least now you have to put the loop out in the open where you can find it.

Also note how I converted the Load() method to static.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1