Why does my model go out to the DB again for related properties which have already been loaded in a separate LINQ join statement?
Background info:
I recently ran some Performance profiling on my ASP.NET MVC3 Linq application I got some puzzling results that I hope to resolve.
Here is a simplified version of db structure. Foreign key relationships are set up.
Table Client:
ID|Client Name|Vertical ID|
___________________________
1 |Client1 |2 |
2 |Client2 |5 |
3 |Client3 | |
Table Vertical
ID|Vertical Name|
_________________
2 |Life |
5 |Guilt |
When I display the list of clients, I am displaying them the client name and the vertical. My Controller is simple:
public ActionResult Index()
{
...
var model = repository.GetAllClients();
return View(model);
}
Get all clients runs the join on the vertical table (as well as a few others).
public IEnumerable<client> GetAllClients()
{
OperationsMetricsDataContext db = new OperationsMetricsDataContext();
var clients = from c in db.clients
join v in db.verticals on c.vertical equals v into tmp1
from v in tmp1.DefaultIfEmpty()
join sm in db.cel_sm_staffs on c.sm_id equals sm.cel_sm_id into tmp2
from sm in tmp2.DefaultIfEmpty()
join cel in db.cel_sm_staffs on c.cel_id equals cel.cel_sm_id into tmp3
from cel in tmp3.DefaultIfEmpty()
select c;
return clients;
}
In my View, I then attempt to display the vertical name I have bound using this method in my partial client class
public string getVerticalName()
{
return this.vertical == null ? "N/A" : this.vertical.vertical_name;
}
The problem:
@foreach (var item in Model) {
<tr>
<td>
@Html.Raw(item.client_name)
</td>
<td>
@Html.Raw(item.getVerticalName())
</td>
The problem that I uncovered is that even though I specified the join in the original model, (and I’ve been able to verify that the vertical property has a value loaded before the model is passed to the view), each time I call the getVerticalName method, Linq automatically goes out to the db again to load the vertical so that I can access the vertical name. This is pretty inefficient because I end up with a separate db call for each client just to get their vertical information.
What am I missing here? Shouldn’t LINQ know that the property is already bound and not go out to the db to retrieve it or is there something I need to specify to tell LINQ that my information already exists in the model?
Not sure how it behaves in MVC, but perhaps your problem is lazy loading the related entity.
You can force Linq-2-sql to eagerly load using LoadOptions.
Try