How to track down Duplicate Linq to SQL Queries identified by MiniProfiler?
I've wired up the MvcMiniProfiler to my app, and it's reporting Duplicate Queries.
I've set a BreakPoint in my Repository
Public Function Read() As System.Linq.IQueryable(Of [Event]) Implements IEventRepository.Read
Dim events = (From e In dc.Events
Select e)
Return events.AsQueryable ''# BREAKPOINT HERE
End Function
And I've hit the page in question.
My code hits the Read()
function twice through my service layer (this is by design since I can't figure out how to reduce the calls)
Dim eventcount = EventService.GetHotEventCount() ''# First Hit
Dim eventlist = EventService.GetHotEvents((page - 1) * 5) ''# Second Hit
Dim model As EventsIndexViewModel = New EventsIndexViewModel(eventlist, page, eventcount)
Return View("Index", model)
The EventService
does a simple query against the IQueryable Read
Public Function GetHotEvents(ByVal skip As Integer) As List(Of Domain.Event) Implements IEventService.GetHotEvents
Return _EventRepository.Read() _
.Where(Function(e) e.EventDate >= Date.Today AndAlso
e.Region.Name = RegionName) _
.OrderByDescending(Function(e) (((e.TotalVotes) * 2) + e.Comments.Count)) _
.ThenBy(Function(e) e.EventDate) _
.Skip(skip) _
.Take(5) _
.ToList()
End Function
Unfortunately I can't figure out why MiniProfiler is saying there are 8 Duplicate queries (13 in total).
Revised
So it appears as though Sam has stated that I'm not pre-loading my relationships within my queries.How do I appropriately pre-load relationships in Linq to SQL? Can anyone lend any advice?
Edit
Here's the ViewModel that's being created.Public Class EventsIndexViewModel
Public Property Events As List(Of Domain.ViewModels.EventPreviewViewModel)
Public Property PageNumber As Integer
Public Property TotalEvents As Integer
Public Property MapEventsList As List(Of Domain.Pocos.MapPin)
Public Property JsonMapEventsList As String
Sub New()
End Sub
Sub New(ByVal eventlist As List(Of Domain.Event), ByVal page As Integer, ByVal eventcount As Integer)
_PageNumber = page
__TotalEvents = eventcount
Dim mel As New List(Of MapPin)
_Events = New List(Of Domain.ViewModels.EventPreviewViewModel)
For Each e In eventlist
_Events.Add(New Domain.ViewModels.EventPreviewViewModel(e)开发者_StackOverflow)
mel.Add(New MapPin(e.Location.Latitude, e.Location.Longitude, e.Title, e.Location.Name, e.Location.Address))
Next
_MapEventsList = mel
_JsonMapEventsList = (New JavaScriptSerializer()).Serialize(mel)
End Sub
End Class
Edit - added screenshot
You basically have two options to avoid SELECT n+1 with LINQ to SQL:
1) Use DataLoadOptions - http://msdn.microsoft.com/en-us/library/system.data.linq.dataloadoptions.loadwith.aspx
DataLoadOptions enables you to specify per entity exactly that related tables should be eager-loaded. In your case, for the entity Event, you could specify LoadWith for both Comments and Locations. Whenever you load Events, Comments and Locations will then be preloaded.
The DataLoadOptions is a property you can set on the DataContext itself.
2) Use projection to fetch all the data you need in one specific query, instead of relying on lazy loading the related entities.
You have imposed a repository on top of your DataContext, so this might not be the approach you want to take, but:
Instead of selecting a list of Events and then using this entity's properties Comments and Locations, you could have your query return exactly what you need in a specific ViewModel class. LINQ to SQL would then fetch everything in a single SQL query.
I consider this the best approach if you don't absolutely NEED to abstract away the DataContext behind a repository interface. Even if you do, you could consider having the repository return View specific results, i.e.
dc.Events
.Where(something)
.Skip(something)
.Select(event => new EventViewModel
{
Event = event
Locations = event.Locations,
Comments = event.Comments
}
);
with EventViewModel being
public class EventViewModel
{
Event Event;
List<Location> Locations;
List<Comment> Comments;
}
you're going to want to .Include("Locations")
and .Include("Comments")
in the respective queries. I believe it goes before the .Where()
, but I'm not positive about that.
精彩评论