How to implement one to many relationship

Luke101 picture Luke101 · Dec 9, 2014 · Viewed 11.6k times · Source

I have a one to many relationship coming from a stored procedure. I have several one to many relationships in the query and i am trying to map these fields to a C# object. The problem i am having is i get duplicate data because of the one to many relationships. Here is a simplified version of my code:

Here is the objects classes:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Color> FavoriteColors { get; set; }
    public List<Hobby> Hobbies { get; set; }

    public Person()
    {
        FavoriteColors = new List<Color>();
        Hobbies = new List<Hobby>();
    }
}

public class Color
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Hobby
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Here is how I am retrieving the data:

using (SqlConnection conn = new SqlConnection("connstring.."))
{
    string sql = @"
                    SELECT 
                        Person.Id AS PersonId, 
                        Person.Name AS PersonName, 
                        Hobby.Id AS HobbyId,
                        Hobby.Name AS HobbyName,
                        Color.Id AS ColorId,
                        Color.Name AS ColorName
                    FROM Person
                    INNER JOIN Color on Person.Id = Color.PersonId
                    INNER JOIN Hobby on Person.Id = Hobby.PersonId";
    using (SqlCommand comm = new SqlCommand(sql, conn))
    {
        using (SqlDataReader reader = comm.ExecuteReader(CommandBehavior.CloseConnection))
        {
            List<Person> persons = new List<Person>();
            while (reader.Read())
            {
                Person person = new Person();
                //What to do
            }
        }
    }
}

As you can see there can be multiple colors and hobbies for a given Person. Usually, I would use Entity Framework to solve this mapping but we are not allowed to use any orms. Is there a technique to properly unflatten this data?

Answer

koder picture koder · Dec 9, 2014

The idea is while iterating on the reader check if the existing row person id exists in the person list. If not create a new person object and declare two separate lists to hold the hobby and color info. For subsequent iterations go on populating these two lists because these will always be the same persons data. One you get to a new record for a new person, add these lists to the person object and start over with a new person object

Below is the sample code:

                string sql = @"
                SELECT 
                    Person.Id AS PersonId, 
                    Person.Name AS PersonName, 
                    Hobby.Id AS HobbyId,
                    Hobby.Name AS HobbyName,
                    Color.Id AS ColorId,
                    Color.Name AS ColorName
                FROM Person
                INNER JOIN Color on Person.Id = Color.PersonId
                INNER JOIN Hobby on Person.Id = Hobby.PersonId
                Order By PersonId"; // Order By is required to get the person data sorted as per the person id
            using (SqlCommand comm = new SqlCommand(sql, conn))
            {
                using (SqlDataReader reader = comm.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    List<Person> persons = new List<Person>();
                    while (reader.Read())
                    {
                        var personId = reader.GetInt32(0);
                        var personName = reader.GetString(1);
                        var hobbyId = reader.GetInt32(3);
                        var hobbyName = reader.GetString(4);
                        var colorId = reader.GetInt32(5);
                        var colorName = reader.GetString(6);

                        var person = persons.Where(p => p.Id == personId).FirstOrDefault();
                        if (person == null)
                        {
                            person = new Person();
                            person.Id = personId;
                            person.Name = personName;

                            hobby = new Hobby() { Id = hobbyId, Name = hobbyName };
                            color = new Color() { Id = colorId, Name = colorName };

                            person.FavoriteColors = new List<Color>();
                            person.Hobbies = new List<Hobby>();

                            person.FavoriteColors.Add(color);
                            person.Hobbies.Add(hobby);

                            persons.Add(person);
                        }
                        else
                        {
                            hobby = new Hobby() { Id = hobbyId, Name = hobbyName };
                            color = new Color() { Id = colorId, Name = colorName };

                            //JT Edit: if the colour/hobby doesn't already exists then add it
                            if (!person.FavoriteColors.Contains(color))
                               person.FavoriteColors.Add(color);

                            if (!person.Hobbies.Contains(hobby))
                               person.Hobbies.Add(hobby);
                        }
                    }
                }
            }
        }