EF Core generates non-optimized SQL for .Include().First() queries
See original GitHub issuePomelo is not correctly generating SQL code. The following code produces the following SQL:
SELECT 't'.'UserId', 't'.'Username', 'o'.'OrderId', 'o'.'Total', 'o'.'UserId'
FROM (
SELECT 'u'.'UserId', 'u'.'Username'
FROM 'Users' AS 'u'
WHERE 'u'.'UserId' = 1
LIMIT 1
) AS 't'
LEFT JOIN 'Orders' AS 'o' ON 't'.'UserId' = 'o'.'UserId'
ORDER BY 't'.'UserId'
There is absolutely no reason that a second select statement is being generated. It is redundant and as far as I am aware it is a performance problem. It should be generating the following SQL code:
SELECT u.userid, u.username, o.orderid, o.total, o.userid
FROM users AS u
JOIN orders AS o ON o.userid = u.userid
WHERE u.userid = 1
C# Dummy code
namespace LearningCSharp1
{
class Program
{
public static async Task Main(string[] args)
{
var context = new Context();
var user = await context.Users
.Where(u => u.UserId == 1)
.Include(u => u.Order)
.FirstAsync();
}
}
class User
{
public int UserId { get; set; }
public string Username { get; set; } = null!;
public IEnumerable<Order> Order { get; set; } = new List<Order>();
public override string ToString()
{
return $"Id: {UserId} | Username: {Username}";
}
}
class Order
{
public int OrderId { get; set; }
public decimal Total { get; set; }
public User User { get; set; } = null!;
public int UserId { get; set; }
}
class Context : DbContext
{
public DbSet<User> Users => Set<User>();
public DbSet<Order> Orders => Set<Order>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMySql(
"Server=localhost;Database=efcore;User=root;Password=password.",
ServerVersion.AutoDetect("Server=localhost;Database=efcore;User=root;Password=password.")
)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
}
}
}
Further technical details
Pomelo.EntityFrameworkCore.MySql version: 6.0.1 Microsoft.AspNetCore.App version: 6.0.6
Issue Analytics
- State:
- Created a year ago
- Comments:9
Top Results From Across the Web
EF Core 1.0 - Include() generates more than one queries
I am using EF 7.0.0-rc1-final. The following statement generates multiple queries on the server. Is this normal or I am missing something ?...
Read more >SQL Queries - EF Core
SQL queries are useful if the query you want can't be expressed using LINQ, or if a LINQ query causes EF to generate...
Read more >Query IncludeOptimized in Entity Framework Plus (EF Plus)
The SQL generated by EF+ IncludeOptimized performs multiple SELECT but drastically decreases the amount of data transferred.
Read more >Querying in Entity Framework Core
Querying in Entity Framework Core remains the same as in EF 6.x, with more optimized SQL queries and the ability to include C#/VB....
Read more >Writing Better Performing Queries with LINQ on EF Core 6.0 ⚙️
With LINQ, a query is a first-class language construct, just like classes, ... for EF generate the needed groupings, and can have an...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@smitpatel Good catch, that’s it! And that is why the statement needs to be generated by EF Core the way it already currently is, since otherwise only the first
Order
entity would be returned, which would be an incorrect translation of the LINQ query.So looks good and works as expected.
They don’t produce same SQL either. The LIMIT in both queries is different number.
Now I realized why perf numbers are that way @lauxjpn . Both are running different SQL which generates different results. MultiSelect version takes first User and associated orders which can be one or more so result set could have 5 rows too. SingleSelect is doing Limit 1 on top level so it will only take first User and first matching order and discard other orders if they are matching and should be included in the result when populating Orders collection via EF. Hence it is only selecting 1 row and not 5. Hence results makes sense, why SingleSelect is faster.