Принудительное внутреннее соединение из EF Cores Include () для необязательного отношения - PullRequest
1 голос
/ 26 апреля 2019

Мы изменили отношение с обязательного на необязательное, теперь результирующий SQL, созданный функцией Include () EF Core, выполняет левое внешнее, а не внутреннее соединение. Проблема заключается в том, что эти необязательные сущности имеют требуемые фильтры запросов.

Допустим, у нас есть следующее:

public class First 
{
    public int? SecondId { get; set; }
}
public class Second 
{
    public First First { get; set; }
    public int ThirdId { get; set; }
}
public class Third
{
    public Second Second { get; set; }
    public string Tenant { get; set; }
}

public class MyContext : DbContext 
{
    protected readonly string _tenant;
    ...
    modelBuilder.Entity<Third>(p =>
    {
        p.HasQueryFilter(x => Tenant == _tenant);
    });
    ...
}

И тогда мы делаем следующее:

MyContext.First.Include(p => p.Second).ThenInclude(p => p.Third);

Это создаст левое внешнее соединение, так как связь необязательна. Это, конечно, тогда обойдет фильтр запросов. Есть ли способ сделать так, чтобы вместо этого было включено INNER JOIN?

В настоящее время это решается добавлением еще нескольких условий в where:

.Where(p => p.Second.Third.Tenant == _tenant);

Но это нежелательно, так как в некоторых крайних случаях _tenant имеет значение null и будет давать неверные данные.

Я знаю, что могу перевернуть это и пойти на

MyContext.Third.Include() ...

Но это также нежелательно, так как First в этом сценарии имеет много других связанных данных, и я не хочу бесконечно связывать Include (). ThenInclude () до абсурда.

Могу ли я форсировать внутренние объединения дополнительными объектами? Или я должен вручную написать SQL для этого?

1 Ответ

2 голосов
/ 26 апреля 2019

Могу ли я форсировать внутренние объединения дополнительными объектами?

Вы не можете.А ты не должен .Потому что, хотя inner join, вероятно, решит ваш конкретный случай, в целом он отфильтрует все зависимые объекты, имеющие null FK (например, First.SecondId == null), что противоречит всей концепции необязательных отношений.

Проблема, которую я вижу, заключается в том, что вы, похоже, пытаетесь использовать Include для фильтрации.По идее Include это то, что он говорит - для каждой сущности, возвращаемой запросом, также включают соответствующие данные.Он не предназначен для фильтрации ни сущности, ни связанных данных.

Так что вам нужен фильтр запросов.

Фактическая проблема заключается в том, что EF Core Global Query Filters не поддерживают критерии, основанные на свойствах навигации .Вот почему люди в таких сценариях нарушают нормализацию (вводят избыточность) и помещают TenantId свойство (столбец) в каждую сущность (таблицу), что позволяет им устанавливать глобальный фильтр для каждой сущности.

С учетом сказанного,явный фильтр запросов (Where) в настоящее время является единственной опцией.

   .Where(p => p.Second.Third.Tenant == _tenant);

Но это нежелательно, поскольку в некоторых крайних случаях _tenant имеет значение null и затем выдаст неверные данные.

Ну, вам просто нужны правильные критерии, учитывающие необязательные отношения, например

.Where(p => p.SecondId == null || p.Second.Third.Tenant == _tenant);

Но это на самом деле показывает проблему отсутствия Tenant для каждой сущности - когда First.SecondId == null, вы не можете сказать, какой Tenant владеет First.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...