-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query: Nullable foreign-key can lead to client evaluation #7616
Comments
Configuring
I'm analyzing client evaluations of a project and this seems to be a common pattern:
Maybe the second ones are only subsequent errors, but they should disappear too, of course. 🤔 BTW, I know the sample doesn't make much sense, I'm just trying to reproduce these warnings. Let me know, if you need more elaborated code... |
This code produces more warnings (just to make sure): using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.SqlClient;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.Logging;
// --- Models ------------------------------------------------------------------
public class Parent
{
[Key]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public ParentInfo Info { get; set; }
public ISet<ParentChild> Children { get; } = new HashSet<ParentChild>();
}
public class ParentInfo
{
[Key]
public Guid Id { get; set; }
[Required]
public Guid? ParentId { get; set; }
public Parent Parent { get; set; }
public Guid? PartnerId { get; set; }
public Parent Partner { get; set; }
[Required]
public string Name { get; set; }
}
public class ParentChild
{
[Key]
public Guid Id { get; set; }
[Required]
public Guid? ParentId { get; set; }
public Parent Parent { get; set; }
[Required]
public Guid? ChildId { get; set; }
public Child Child { get; set; }
}
public class Child
{
[Key]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public ISet<ParentChild> Parents { get; } = new HashSet<ParentChild>();
}
// --- Context -----------------------------------------------------------------
public class ModelContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasOne(parent => parent.Info)
.WithOne(info => info.Parent)
.OnDelete(DeleteBehavior.Restrict);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionStringBuilder = new SqlConnectionStringBuilder()
{
DataSource = "(localdb)\\MSSQLLocalDB",
InitialCatalog = "EFCore",
IntegratedSecurity = true
};
optionsBuilder.UseSqlServer(connectionStringBuilder.ConnectionString);
optionsBuilder.ConfigureWarnings(warnings =>
warnings.Log(RelationalEventId.QueryClientEvaluationWarning)
);
}
}
// --- Test --------------------------------------------------------------------
public class Program
{
public static void Main(string[] args)
{
// Set up database
using (var context = new ModelContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
}
// Create query
using (var context = new ModelContext())
{
context.GetService<ILoggerFactory>().AddConsole();
var childId = Guid.NewGuid();
var query = from parent in context.Parents
let hasPartner = parent.Info.Partner.Children.Any(child => child.ChildId == childId)
where parent.Children.Any(child => child.ChildId == childId)
select new
{
Id = parent.Id,
Name = parent.Name,
Info = parent.Info.Parent,
Someone = (from child in parent.Info.Partner.Children
where child.Id != childId
select child.Child.Name).FirstOrDefault(),
Partner = hasPartner ? parent.Info.Partner.Name : null
};
query.ToList();
}
}
} |
Like #7922 this is now working in the dev branch. No client side evaluation is performed in the sample code provided. |
Also verified that it works fine in the dev bits, we create the following queries: First repro: SELECT [parent.Partner].[Id], [parent.Partner].[Name], [parent.Partner].[PartnerId], [parent].[Id], [parent].[Name], CASE
WHEN (
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM [ParentChild] AS [child1]
INNER JOIN [Children] AS [child.Child1] ON [child1].[ChildId] = [child.Child1].[Id]
WHERE ([child.Child1].[Name] = N'Narf') AND ([parent.Partner].[Id] = [child1].[ParentId]))
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
) = 1
THEN [parent.Partner].[Name] ELSE NULL
END
FROM [Parents] AS [parent]
LEFT JOIN [Parents] AS [parent.Partner] ON [parent].[PartnerId] = [parent.Partner].[Id]
WHERE EXISTS (
SELECT 1
FROM [ParentChild] AS [child]
INNER JOIN [Children] AS [child.Child] ON [child].[ChildId] = [child.Child].[Id]
WHERE ([child.Child].[Name] = N'Narf') AND ([parent].[Id] = [child].[ParentId])) Second repro: SELECT [parent.Info.Parent].[Id], [parent.Info.Parent].[Name], [parent.Info.Partner].[Id], [parent.Info.Partner].[Name], [parent].[Id], [parent].[Name], (
SELECT TOP(1) [child.Child0].[Name]
FROM [ParentChild] AS [child2]
INNER JOIN [Children] AS [child.Child0] ON [child2].[ChildId] = [child.Child0].[Id]
WHERE ([child2].[Id] <> @__childId_2) AND ([parent.Info.Partner].[Id] = [child2].[ParentId])
), CASE
WHEN (
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM [ParentChild] AS [child3]
WHERE ([child3].[ChildId] = @__childId_0) AND ([parent.Info.Partner].[Id] = [child3].[ParentId]))
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
) = 1
THEN [parent.Info.Partner].[Name] ELSE NULL
END
FROM [Parents] AS [parent]
LEFT JOIN [ParentInfo] AS [parent.Info] ON [parent].[Id] = [parent.Info].[ParentId]
LEFT JOIN [Parents] AS [parent.Info.Parent] ON [parent.Info].[ParentId] = [parent.Info.Parent].[Id]
LEFT JOIN [Parents] AS [parent.Info.Partner] ON [parent.Info].[PartnerId] = [parent.Info.Partner].[Id]
WHERE EXISTS (
SELECT 1
FROM [ParentChild] AS [child]
WHERE ([child].[ChildId] = @__childId_1) AND ([parent].[Id] = [child].[ParentId])) |
Using nullable foreign-keys seems to lead to client evaluation more often.
Steps to reproduce
Further technical details
EF Core version: 1.1.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 7
IDE: Visual Studio 2015
The text was updated successfully, but these errors were encountered: