Child Model List is Empty after DB Add in Entity Framework Core

asp.net-mvc c# entity-framework-core

Question

So I am trying out Entity Framework Core, and am having trouble with my first pair of relational Models. I'm trying to use conventions to form the relationship, as follows:

The principal entity, with conventional principal key (Id) and collection navigation property (the list of Locations):

public class Site
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public int LocationCount { get; set; } = 1;
    public List<Location> Locations { get; set; } = new List<Location>();
}

The dependent entity, with conventional foreign key (SiteId) and reference navigation property (Site):

public class Location
{
    public int Id { get; set; }
    public int SiteId { get; set; }
    public Site Site { get; set; }
    public int AreaCount { get; set; } = 1;
    public Location(Site site) { SiteId = site.Id; }
}

And since I want every Site to already have at least one location, I put this in the Create action of the SiteController, in an attempt to create the dependents along with the principal:

[HttpPost]
public IActionResult Create(Site site)
{
    _context.Sites.Add(site);
    for (int i = 0; i < site.LocationCount; i++)
        _context.Locations.Add(new Location(site));
    _context.SaveChanges();
    return RedirectToAction("Details", new { Name = site.Name });
}

However, on the Details view, when I try to display the Locations that were created, the list is always empty:

<h2>@Model.Locations.Count Locations</h2>    
@foreach (Location l in Model.Locations)
{
    <div class="row">
        <div class="col-sm-12">@l.Id</div>
    </div>
}

Just results in:

0 Locations

Thank you in advance for any help explaining what I've missed!

Edit: By request, here is the body of the Details action in the Site Controller, which gets the Model data for the Details View:

public IActionResult Details(string name)
{
    var model = _context.Sites.FirstOrDefault(a => a.Name == name);
    if (model == null) model = new Site();
    return View(model);
}

Also, per one suggestion, I have fixed the problem of setting the Location.SiteId before Site.Id has a value, since the Id isn't automatically generated on the Site until after SaveChanges() is called. Unfortunately, the result (empty Locations list) is still the same:

[HttpPost]
public IActionResult Create(Site site)
{
    int nextsiteid = _context.Sites.Count() + 1;
    _context.Sites.Add(site);
    for (int i = 0; i < site.LocationCount; i++)
        _context.Locations.Add(new Location(siteid));
    _context.SaveChanges();
    return RedirectToAction("Details", new { Name = site.Name });
}

And changed the Location constructor to use the Id instead of the instance:

public Location(int siteid) { SiteId = siteid; }

Thanks in advance for any further suggestions!

1
3
6/4/2017 1:12:38 PM

Accepted Answer

I'm just transcribing the answer I was given, which was provided in the comments of the original post, so that it can easily be found by anyone reading the post.

The issue was, quite simply, that EF Core does not yet support lazy loading. So when the Site entity is populated, it doesn't lazily load the list of dependent locations into the collection navigation property.

The fix is to use eager loading, by way of:

var model = _context.Sites.Include(s => s.Locations).FirstOrDefault(a => a.Name == name);

When loading the Model data of the Site to provide to the View.

Thanks for the solution, Ivan!

4
6/4/2017 4:18:56 PM

Popular Answer

if the Lazy Loading is true. No need to loop over or navigate its child. its will save your parent and as well its child. make sure are you getting parents and its child

[HttpPost]
public IActionResult Create(Site site)
{  // debug here are you getting parent & its child ?
_context.Sites.Add(site);
   // for (int i = 0; i < site.LocationCount; i++)  // No need of it
   // _context.Locations.Add(new Location(site));   // No need of it
_context.SaveChanges();
return RedirectToAction("Details", new { Name = site.Name });
}

Updated: if these two tables are linked with each other & you are manually trying to add location table . Let me give you a simple , hope this will help you

[HttpPost]
public IActionResult Create(Site site)
{  // debug here are you getting parent & its child ?
_context.Sites.Add(site);
   // get the last added site id

   int? maxId=
  _context.Sites.orderbyDecending(x=>x.SiteId).FirstorDefault(x=>x.SiteId);// may be in this line typing mistake , you change it according to linq
    foreach(var i in site.Location) 
    {
      // in this loop traverse the child array and override the foreign key id with last added id
       i.SiteId =maxId;
      _context.Locations.Add(i);
    }

_context.SaveChanges();
return RedirectToAction("Details", new { Name = site.Name });
}


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow