Я полагаю, что для этого есть причина, хотя я могу не видеть полную картину и, возможно, просто размышлять.
Ситуация
Действительно, единственнаяспособ (который я вижу) для EF, чтобы иметь возможность перечислять только не старших разработчиков (ваш вариант использования запроса) в сценарии TPT, читая только таблицу Developer
, был бы с помощью дискриминатора, и мы знаем, что EFне использует его в стратегиях TPT / TPC.
Почему?Хорошо, помните, что все старшие разработчики являются разработчиками, поэтому вполне естественно (и необходимо), чтобы у них была запись Developer
, а также запись SeniorDeveloper
.
Единственное исключение - если Developer
является абстрактным типом, в этом случае вы можете использовать стратегию TPC для полного удаления таблицы Developer
.Однако в вашем случае Developer
является конкретным.
Текущее решение
Помня об этом, и без дискриминатора в таблице Developer
,единственный способ определить, является ли какой-либо разработчик не старшим разработчиком, это проверить, является ли он не старшим разработчиком;Другими словами, проверяя, что в таблице SeniorDeveloper
или в любой другой таблице подтипов нет записи о разработчике.
Это звучало немного очевидно, но теперь мы понимаем, почему SeniorDeveloperТаблица должна использоваться и доступна, когда ее базовый тип (Разработчик) является конкретным (не абстрактным).
Текущая реализация
Я пишу этоиз памяти, так что я надеюсь, что это не слишком прочь, но это также то, что Слаума упомянул в другом комментарии.Возможно, вы захотите запустить профилировщик SQL и проверить это.
Способ, которым это реализовано, - запрос UNION из проекций таблиц.Эти проекции просто добавляют дискриминатор, объявляющий их собственный тип некоторым закодированным способом [1].В объединенном наборе строки могут быть отфильтрованы на основе этого дискриминатора.
[1] Если я правильно помню, это выглядит примерно так: 0X для базового типа, 0X0X для первого подтипав объединении, 0X1X для второго подтипа и т. д.
Компромисс # 1
Мы уже можем определить компромисс: EFможет либо хранить дискриминатор в таблице, либо «генерировать единицу» во время выполнения.
- Недостатки хранимого дискриминатора заключаются в том, что он менее эффективен в пространстве и, возможно, «уродлив».(если это аргумент).Преимущества - производительность поиска в очень специфическом случае (нам нужны только записи типа base ).
- Недостатки дискриминатора "времени выполнения" заключаются в том, чтоэта производительность поиска не так хороша для того же варианта использования.Преимущества заключаются в том, что он более компактен.
На первый взгляд может показаться, что иногда мы предпочитаем тратить немного места на производительность запросов, а EF нам этого не позволяет.
В действительности не всегда ясно, когда;запрашивая UNION из двух таблиц, мы просто ищем два индекса вместо одного, и разница в производительности незначительна.С одним уровнем наследования это не может быть хуже, чем 2x (так как все наборы подтипов не пересекаются).Но подождите, это еще не все.
Компромисс № 2
Помните, что я сказал, что преимущество в производительности подхода с сохраненным дискриминатором появится только в конкретном использовании.случай, когда мы ищем записи базового типа.Почему это так?
Что ж, если вы ищете разработчиков, которые могут быть или не быть старшими разработчиками [2], вам все равно придется искать SeniorDeveloper
таблицу в любом случае .Хотя это, опять же, кажется очевидным, то, что может быть менее очевидным, это то, что EF не может знать заранее, будут ли типы только одного типа или другого.Это означает, что в худшем случае он должен будет выполнить два запроса: один в таблице Developer
, а если в наборе результатов есть хотя бы один старший разработчик, то второй в SeniorDeveloper
таблица.
К сожалению, дополнительная передача туда и обратно , вероятно, оказывает большее влияние на производительность, чем объединение двух таблиц. (Я говорю, вероятно, я не проверял это.) Хуже, это увеличивается для каждого подтипа, для которого есть строка в наборе результатов. Представьте себе тип с 3, 5, или даже 10 подтипами.
И это ваш компромисс № 2.
[2] Помните, что этот вид операции может происходить из любой части вашего приложения, в то время как решение компромисса должно выполняться глобально, чтобы удовлетворить все процессы / приложения / пользователи. Также соедините это с тем фактом, что команда EF должна сделать эти компромиссы для всех пользователей EF (хотя это правда, что они могли бы добавить некоторую конфигурацию для компромиссов такого рода).
Возможная альтернатива
Путем пакетирования SQL-запросов можно было бы избежать многократных циклических переходов. EF должен был бы отправить некоторую процедурную логику на сервер для условного поиска (T-SQL). Но поскольку в компромиссе № 1 мы уже установили, что преимущество в производительности, скорее всего, незначительно во многих случаях, я не уверен, что это когда-нибудь стоило бы усилий. Может быть, кто-то может открыть для этого тикет, чтобы определить, имеет ли это смысл.
Заключение
В будущем, возможно, кто-то может оптимизировать несколько типичных операций в этом конкретном сценарии с помощью некоторых творческих решений, а затем предоставить некоторые переключатели конфигурации, когда оптимизация предполагает такие компромиссы.
Однако сейчас я думаю, что EF выбрала справедливое решение. Странным образом, это почти чище.
Несколько заметок
Я считаю, что использование union является оптимизацией, применяемой в определенных случаях. В других случаях это будет внешнее соединение, но использование дискриминатора (и всего остального) остается прежним.
Вы упомянули множественное наследование, что изначально меня смутило. В общем объектно-ориентированном языке множественное наследование - это конструкция, в которой тип имеет несколько базовых типов. Многие объектно-ориентированные системы типов не поддерживают это, включая CTS (используется всеми языками .NET). Вы имеете в виду что-то еще здесь.
Вы также упомянули, что EF "отступит" к стратегии TPT. В случае Developer / SeniorDeveloper стратегия TPC будет иметь те же результаты, что и стратегия TPT, поскольку Developer является конкретным. Если вы действительно хотите одну таблицу, вы должны использовать стратегию TPH.