Mark
Mark

Reputation: 7818

How to Display Hierarchial data in MVC Razor view

I'm trying to display hierarchial data in my view - I have 2 models (parent/child linked)

Models:

public class Clinic
{
    public int ClinicId { get; set; }
    [Display(Name="Clinic Name")]
    public string Name { get; set; }
    public string Description { get; set; }
    public List<Property> Properties { get; set; }

}

public class Property
{
    public int PropertyID { get; set; }
    public int ClinicId { get; set; }
    [Display(Name="Property Address")]
    public string Address { get; set; }
    public Clinic Clinic { get; set; }
    public List<Room> Rooms { get; set; }
 }

public class Room
{
    public int RoomId { get; set; }
    public int PropertyId { get; set; }
    public string RoomName { get; set; }
    public Property Property { get; set; }

}

My controller is builds this Linq query - which displays fine in LinqPad:

    //
    // GET: /Property/Overview/
    public ActionResult Overview()
    {
   // original code - replaced following help below
   //  var ovw =
   //         (from c in db.Clinics
   //          select new
   //          {
   //              c.Name,
   //              Properties =
   //                  from p in db.Properties
   //                  where p.ClinicId == c.ClinicId
   //                  select new
   //                  {
   //                      p.Address
   //                  }
   //                  });

        IEnumerable<Clinic> ovw = from c in db.Clinics
        select c;

        return View(ovw);
    }

My view is:

@model IEnumerable<ttp.Models.Clinic>

@{
    ViewBag.Title = "Overview";
}

<h2>Overview</h2>

 @foreach (var item in Model) {
 @item.Name
        <br />
 if (item.Properties != null){
    foreach (var item2 in item.Properties) {
        @item2.Address <br />

 if (item2.Rooms != null){
    foreach (var item3 in item2.Rooms) {
        @item3.RoomName <br />
     }

     }
    }
   }

It should display:

Clinic1
   Property1
      Room1
      Room2
      Room3

   Property2
      Room4
      Room5

Clinic2
   Property3
      Room6
....

But instead just shows:

Clinic1
Clinic2
Clinic3

This will only ever populate the first level of data of Clinics - not the child Properties, or the child Rooms of Properties.

Can anyone please help me fix my controller or my view?

Thanks, Mark

Upvotes: 1

Views: 1613

Answers (3)

Mark
Mark

Reputation: 7818

I've plus 1'd the answers above, as they are very helpful - but the actual problem/answer was in my model definitions - to access the linked tables in the controller/view, I had to add VIRTUAL to the linked property:

Where I had:

public List<Property> Properties { get; set; } 

That should have been:

public virtual ICollection<Property> Properties { get; set; }

I hope this helps someone else with this problem.

Thanks to Steve and Ufuk for your help, and time.

Mark

Upvotes: 0

Ufuk Hacıoğulları
Ufuk Hacıoğulları

Reputation: 38488

You should create instances of Clinic and Property classes. You are using anonymous types.

Also don't forget to call ToList() for Properties because your query will return IEnumerable<Property> or IQueryable<Property>.

IEnumerable<Clinic> ovw = from c in db.Clinics
                          select new Clinic
                          {
                            Name = c.Name,
                            Properties =(from p in db.Properties
                                         where p.ClinicId == c.ClinicId
                                         select new Property
                                         {
                                             Address = p.Address
                                         }).ToList()
                          });

Upvotes: 4

Steve Owen
Steve Owen

Reputation: 2071

You're not actually returning a list of Clinics because you've used

select new { ... }

Instead you want to just get the clinics. The model and collection already exists. There's no need to recreate it:

var ovw = db.Clinics;

If you're trying to have a view model which is separate from the db model (which is good practice) look into AutoMapper or manually map from the db Clinic to a view model Clinic. Then instead of select new { ... } you would use

select new vmClinic { Name = c.Name, ... }

where vmClinic is your viewmodel clinic.

Upvotes: 2

Related Questions