arth81
arth81

Reputation: 229

Implementing hierarchy in the entity model

I need to create a model and method which will be populated with the lists of relevant models.

My model looks like:

public class SearchHierarchyModel  
{
    public IList<Continent> Continent { get; set; }
    public IList<Country> Country { get; set; }
    public IList<City> City { get; set; }
}

My methods should do something like:

public IList<SearchHierarchyModel> GetHierarchyFull(int Coninent_Id)
{
    //pseduocode now
    create lists of countries based on continent id
    create lists of cities based on countries id from the prev. step
    return a list of lists with relevant countries and cities
}

Model classes

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

public class Country
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ContientId { get; set; }
}

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int CityId { get; set; }
}

Perhaps there is a better way to implement such kind of hierarchy? Any ideas how to do this? Maybe the the model should look differently?

Upvotes: 0

Views: 97

Answers (1)

JD Davis
JD Davis

Reputation: 3720

Depending on how your relationships are setup, it would be possible to do something similar to.

public IList<SearchHierarchyModel> GetHierarchyFull(int Continent_Id)
{

    var continent = db.Continents.FirstOrDefault(c => c.Id == Continent_Id);

    //Get all countries with a specified ContinentId
    var countryList = db.Countries.Where(c => c.ContinentId == Continent_Id);

    //Get all cities that have a matching CountryId from any country in the first list.
    var cityList = db.Cities.Where(c => countryList.Any(cl => cl.Id == c.CountryId)).ToList();

    //We need to get the original countryList as a true list rather than a collection of entities.
    //If we had called ToList above, it would error out.
    //If we had called ToList in the ForEach loop, we also would have issues.
    var countryList2 = countryList.ToList();

    var searchList = new List<SearchHierarchyModel>
    {
        new SearchHierarchyModel()
        {
            Continent = new List<Continent> { continent },
            Country = countryList2,
            City = cityList
        }
    };
    return searchList;
}

Now some comments about the above. It seems like to me, you simply want a list of countries and their cities for a given Continent. If that's the case, entity framework makes it even easier for you.

I would change my core models to be:

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

    public virtual ICollection<Country> Countries { get; set; }
} 

public class Country
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ContinentId { get; set; }

    public virtual Continent Continent { get; set; }
    public virtual ICollection<City> Cities { get; set; } 
}

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int CountryId { get; set; }

    public virtual Country Country { get; set; }
}

Then I'd set the hierarchy model to:

public class SearchHierarchyModel
{
    public Continent Continent { get; set; }
    public Country Country { get; set; }
    public City City { get; set; }
}

Now we can change the search function to be:

public IList<SearchHierarchyModel> GetHierarchyFull(int Continent_Id)
{

    var countries = db.Countries.Where(c => c.ContinentId == Continent_Id);
    var cities = db.Cities.Where(c => countries.Any(co => co.Id == c.Id));

    var searchList = new List<SearchHierarchyModel>();
    foreach (var item in cities)
    {
        var newItem = new SearchHierarchyModel
        {
            Continent = item.Country.Continent,
            Country = item.Country,
            City = item
        };
        searchList.Add(newItem);
    }
    return searchList;
}

Now rather than iterating over a list of lists, we're iterating over a list of all the possible return values. Plus with the modification of the models, the relationships between the models are explicitly defined. This means I can more easily reference and interact with them.


And here's a search method that does the traversal of the objects in reverse using Linq, cutting out the foreach loop and removing all the extra lists.

public IList<SearchHierarchyModel> GetHierarchyFull(int Continent_Id)
{
    var continent = db.Continents.FirstOrDefault(c => c.Id == Continent_Id);

    if (continent == null)
        return null;

    var searchList = (from item in continent.Countries
        from city in item.Cities
        select new SearchHierarchyModel
        {
            Continent = continent, 
            Country = item, 
            City = city
        }).ToList();

    return searchList;
}

Upvotes: 2

Related Questions