Performance testing on a recent project led me to redesign my models, and move away from nested models. I’m going to try to detail what I was seeing in hopes that someone can correct my understanding.
I started with a Location model with a nested Address.
public class Location
{
[Key]
public int LocationId { get; set; }
public string Name { get; set; }
public int? AddressId {get;set;}
public virtual Address Address{get;set;}
}
public class Address
{
[Key]
public int AddressId {get;set;}
public string Street1 { get; set; }
public string Street2 { get; set; }
public string Street3 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
The model was populated with LINQ in the controller (cms is my DbContext and contains DbSets for Location and Address):
public ActionResult ListLocations()
{
return View((from l in cms.Locations select l).ToList());
}
The view was a simple loop to display the locations in a table.
<table id="myTable" class="tablesorter">
<thead>
<tr>
<th>Name</th>
<th>City</th>
<th>State</th>
<th>Zip</th>
</tr>
</thead>
<tbody>
@if (Model != null)
{
foreach (var item in Model)
{
<tr>
<td>@item.Name</td>
<td>@item.Address.City</td>
<td>@item.Address.State</td>
<td>@item.Address.Zip</td>
</tr>
}
}
</tbody>
</table>
This method worked as expected, but took on average about 2000ms to complete. Each additional location that I added made the response about 120ms slower.
Using Stack Exchange’s MiniProfiler, I was able to pinpoint each 120ms delay to the first time a address was ‘retrieved’ for display. If subsequent locations had the same address, the performance cost was trivial for repeat displays. The ‘Name’ property retrieval was trivial in all cases.
It appears that the nested model is not retrieved at the same time as the parent model.
When I flattened the model by combining the properties of Address directly into Location. I was able to perform a complete request at an average of 300ms, with trivial changes to this response when adding more locations.
My question – Does Entity Framework provide a way to cache or retrieve nested models all at once or is this lazy instantiation of nested models required by convention? Is there a method for achieving similar response times in a nested model approach vs a flat model?
As always, the possibility of option c) Am I missing something obvious?
Try to use the
Includemethod to include addresses in your query. Just like:http://msdn.microsoft.com/en-us/library/bb738708.aspx
Your code is now executing a query to get the address for each location in your list. If you don´t specify what items you are getting in your query, EF does a lazy loading and each object that it needs will result in a new query.