I have what seems like a pretty straightforward LINQ statement, but the SQL it's generating doesn't make sense to me and it's taking much longer to execute than I'm expecting. I'm just trying to understand what LINQ is doing so I can figure out why it's running so slowly. A comparable SQL statement takes less than a second but the LINQ is taking around 20 seconds.
Here's the code:
// This line takes 20 seconds to return.
var alertEvents = GetFilteredAlertEvents(alert.AlertEvents, db).ToList<AlertEvent>();
private static IEnumerable<AlertEvent> GetFilteredAlertEvents(ICollection<AlertEvent> alertEvents, SlalomEREntities db)
{
Guid marketAlertReceiverGroupId = new Guid(ConfigurationManager.AppSettings["MarketAlertReceiverGroupId"]);
var subQuery = from ae in alertEvents
join tr in db.TargetResources on ae.ResourceId equals tr.ResourceID
join atr in db.AlertTargetResources on tr.ResourceID equals atr.TargetResourceID
where atr.AlertTargetID == marketAlertReceiverGroupId
select ae.AlertEventId;
return from alertEvent in alertEvents
where !subQuery.Contains(alertEvent.AlertEventId)
select alertEvent;
}
In SSMS, the sub-select returns 3126 rows without the where
, and only 127 rows with it. The primary select
in SSMS returns 365 rows without the sub-select. The full select with sub-select returns 238 rows. Not a lot of data.
Using Visual Studio's Diagnostic Tools, I'm seeing 14 SQL statements generated from the LINQ. Every one of them is a simple SQL select and I don't see any joins nor do I see the where
comparison. Here's a sample SQL statement that I'm seeing in the Diagnostic Tools Events window:
SELECT
[Extent1].[AlertTargetID] AS [AlertTargetID],
[Extent1].[TargetResourceID] AS [TargetResourceID],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[CreatedBy] AS [CreatedBy]
FROM [dbo].[AlertTargetResource] AS [Extent1]
There are 13 more similar SQL statements.
Here's the SQL I'm trying to replicate.
select *
from AlertEvent ae1
where ae1.AlertEventId not in
(select ae.AlertEventId
from AlertEvent ae
join TargetResource tr on ae.ResourceId = tr.ResourceID
join AlertTargetResource atr on atr.TargetResourceID = tr.ResourceID
where atr.AlertTargetID = '89bd4ea5-5d56-4b8a-81ba-5a9e5991ba64')
Here are my questions:
In the first part of your method, subQuery
is a query that has not yet been run against the database. In the second part (around your return
statement), you are invoking that query a number of times.
It's not always obvious how Entity Framework will handle a case like this, but here it appears to invoke the query for each item in alertEvents
.
What you really want is the list of IDs returned by the query, and then use that list for the second part of the method. To convert a query to the data returned by that query, you can use ToList()
. This extension method will execute the query and return the data results.
In the code below, subQuery
is now a collection of IDs.
private static IEnumerable<AlertEvent> GetFilteredAlertEvents(ICollection<AlertEvent> alertEvents, SlalomEREntities db)
{
Guid marketAlertReceiverGroupId = new Guid(ConfigurationManager.AppSettings["MarketAlertReceiverGroupId"]);
var subQuery = (from ae in alertEvents
join tr in db.TargetResources on ae.ResourceId equals tr.ResourceID
join atr in db.AlertTargetResources on tr.ResourceID equals atr.TargetResourceID
where atr.AlertTargetID == marketAlertReceiverGroupId
select ae.AlertEventId).ToList();
return from alertEvent in alertEvents
where !subQuery.Contains(alertEvent.AlertEventId)
select alertEvent;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.