简体   繁体   中英

Understanding how LINQ compiles to ADO.NET SQL statements

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:

  1. Why is LINQ genrerating 14 simple selects rather than a single select with joins and the where?
  2. How can I speed up what I thought was a simple bit of code?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM