Одна вещь выделяется и должна быть проверена в реальном коде:
.Where(x => x.Student == null || !x.Student.IsDeleted && x.Student.MyStoreId == storeId)
Если это будет:
.Where(x => x.Student == null || (!x.Student.IsDeleted && x.Student.MyStoreId == storeId))
Свободные условия, подобные этому, могут привести к срабатыванию EF при условииx.Student.MyStoreId независимо от того, есть ученик или нет, что приводит к условию внутреннего соединения.
Редактировать: я пытался воспроизвести эту проблему, и с моей схемой запрос не присоединяется в City to Central School,Вместо этого он присоединяет город к студенческому флакону CentralSchoolId FKs.Я подозреваю, что проблема с вашей ситуацией заключается в том, что в базе данных не определены FK?База данных настроена с помощью кода сначала + миграции или сначала базы данных?
Результирующий запрос:
SELECT TOP (1)
[Extent1].[CityId] AS [CityId],
[Extent1].[Name] AS [Name],
[Extent1].[CentralSchoolId] AS [CentralSchoolId],
[Extent2].[StudentId] AS [StudentId],
[Extent2].[Name] AS [Name1],
[Extent2].[IsDeleted] AS [IsDeleted],
[Extent2].[CentralSchoolId] AS [CentralSchoolId1]
FROM [dbo].[Cities] AS [Extent1]
LEFT OUTER JOIN [dbo].[Students2] AS [Extent2] ON [Extent2].[CentralSchoolId] = [Extent1].[CentralSchoolId]
WHERE ([Extent2].[StudentId] IS NULL) OR ([Extent2].[IsDeleted] <> 1)
Примечание. В моем случае я не отображал StoreId в Student, простоIsDeleted.Кроме того, имя таблицы было Student2 только из-за конфликта имен в моей существующей базе данных области тестирования.
Определения сущностей были идентичны вашим, за исключением сопоставленных PK и добавления IsDeleted в Student.
public class City
{
[Key]
public int CityId { get; set; }
public string Name { get; set; }
[ForeignKey("MyCentralSchool")]
public int? CentralSchoolId { get; set; }
public virtual CentralSchool MyCentralSchool { get; set; }
}
public class CentralSchool
{
[Key]
public int CentralSchoolId { get; set; }
public string Name { get; set; }
[InverseProperty("MyCentralSchool")]
public virtual IList<Student> MyStudents { get; set; }
}
[Table("Students2")]
public class Student
{
[Key]
public int StudentId { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
[ForeignKey("MyCentralSchool")]
public int? CentralSchoolId { get; set; }
public virtual CentralSchool MyCentralSchool { get; set; }
}
Выполнение тестового выражения:
var result = context.Set<City>()
.AsNoTracking()
.SelectMany(x => x.MyCentralSchool.MyStudents.DefaultIfEmpty(), (c, s) => new { City = c, Student = s })
.Where(x => x.Student == null || !x.Student.IsDeleted)
.FirstOrDefault();
Я также запустил Async, и был сгенерирован тот же запрос.Запуск с EF6 для SQL Server.
Редактировать 2: Подтверждено, что существует разница в генерации запросов между EF6 и EF Core.EF Core создает внутреннее соединение между Городом и Центральной школой при разрешении отношений между Городом и Студентом, где EF 6 оптимизирует это, объединяя таблицы через общий FK.Я бы посоветовал поднять это как потенциальную ошибку в EF Core.
Учитывая, что вы хотите получить список всех активных студентов со своими ассоциированными студентами, а также включить все города, в которых нет активных студентов (поэтому будут перечислены все города).а также)
Обходное решение, хотя и некрасивое, для возврата результатов сопоставления в EF Core:
var result2 = context.Set<City>()
.AsNoTracking()
.SelectMany(x => x.MyCentralSchool.MyStudents.DefaultIfEmpty(), (c, s) => new { City = c, Student = s })
.Where(x => !x.Student.IsDeleted)
.Union(context.Set<City>().AsNoTracking().Where(x => x.MyCentralSchool == null || !x.MyCentralSchool.MyStudents.Any(s => !s.IsDeleted))
.Select(x => new { City = x, Student = (Student)null }))
.ToList();