[SQL Server] EF, CreateIfNotExists и T-SQL
От: Olaf Россия  
Дата: 08.09.15 09:12
Оценка: 23 (2)
Добрый день, коллеги!

В рамках ответа на вопрос CreateDatabaseIfNotExists<> не хочет пересоздавать БД, решил посмотреть, каким образом EF проверяет наличие БД на сервере, т.е. к какому вызову T-SQL сводится вызов кода на C#
using (var db = new TestEntities())
{
    db.Database.CreateIfNotExists();
}

Для версий 4.0/5.0 все достаточно стандартно, код проверки на T-SQL выглядит следующим образом:
-- EF 4.0/5.0
SELECT Count(*) FROM sys.databases WHERE [name]=N'Test'

А вот для версии 6.0 реализация изменилась и теперь представляет собой запрос вида:
-- EF 6.0
IF db_id(N'Test') IS NOT NULL SELECT 1 ELSE SELECT Count(*) FROM sys.databases WHERE [name]=N'Test'

Посему у меня возник уже свой вопрос.
Для каких целей автор, в случае отсутствия БД делает дополнительное обращение к sys.databases для ее поиска, ведь функция db_id уже подтвердила отсутствие базы на сервере?

Более реалистичный вариант ответа заключается в неаккуратности реализации проверки в версии 6.0, т.е. автор, планируя изменить подход к анализу существования БД, выполнил работу наполовину. Я конечно могу заблуждаться и запрос все-таки несет какой-то скрытый смысл, но другие варианты ответа выглядят фантастическими – контроль работы db_id или с момента вызова db_id и до перехода к else БД была все-таки создана и т.п.

Самое простое решение, которое напрашивается в этой ситуации
IF db_id(N'Test') IS NOT NULL SELECT 1 ELSE SELECT 0
Re: [SQL Server] EF, CreateIfNotExists и T-SQL
От: BlackEric http://black-eric.lj.ru
Дата: 08.09.15 10:39
Оценка:
Здравствуйте, Olaf, Вы писали:

O>Более реалистичный вариант ответа заключается в неаккуратности реализации проверки в версии 6.0, т.е. автор, планируя изменить подход к анализу существования БД, выполнил работу наполовину. Я конечно могу заблуждаться и запрос все-таки несет какой-то скрытый смысл, но другие варианты ответа выглядят фантастическими – контроль работы db_id или с момента вызова db_id и до перехода к else БД была все-таки создана и т.п.


Возможно, действительно не доделал, хотя зачем было трогать работающую проверку
https://github.com/BlackEric001
Re: [SQL Server] EF, CreateIfNotExists и T-SQL
От: IT Россия linq2db.com
Дата: 08.09.15 13:15
Оценка:
Здравствуйте, Olaf, Вы писали:

O>Самое простое решение, которое напрашивается в этой ситуации

O>
O>IF db_id(N'Test') IS NOT NULL SELECT 1 ELSE SELECT 0
O>


Ещё проще:

SELECT db_id(N'Test')
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: [SQL Server] EF, CreateIfNotExists и T-SQL
От: _ABC_  
Дата: 08.09.15 14:18
Оценка: 27 (2)
Здравствуйте, BlackEric, Вы писали:

BE>Возможно, действительно не доделал, хотя зачем было трогать работающую проверку

Она не работала.

Если у логина нет прав на view any database, то
SELECT Count(*) FROM sys.databases WHERE [name]=N'Test'

выдаст 0. Создание БД тем не менее, разумеется, не сработает.
Отредактировано 08.09.2015 20:10 Does not matter . Предыдущая версия .
Re: [SQL Server] EF, CreateIfNotExists и T-SQL
От: Sinix  
Дата: 08.09.15 14:38
Оценка:
Здравствуйте, Olaf, Вы писали:


O>Для каких целей автор, в случае отсутствия БД делает дополнительное обращение к sys.databases для ее поиска, ведь функция db_id уже подтвердила отсутствие базы на сервере?




Насколько помню, поведение в обоих вариантах совпадает, вне зависимости от разрешений / статуса базы (OFFLINE etc). Подозреваю, самый простой способ узнать — спросить в обсуждениях на codeplex или на stackoverflow.
Re[3]: [SQL Server] EF, CreateIfNotExists и T-SQL
От: Sinix  
Дата: 08.09.15 14:44
Оценка:
Здравствуйте, _ABC_, Вы писали:

_AB>Если у логина нет прав на view any database, то

SELECT Count(*) FROM sys.databases WHERE [name]=N'Test'

_AB>выдаст 0. Создание БД тем не менее не, разумеется, не сработает.
Так DB_ID() тоже будет молчать как партизан в этом случае. В документации раздел Permissions откровенно скопипащщен с sys.databases.

В общем, ниччего не понимаю.
Re[4]: [SQL Server] EF, CreateIfNotExists и T-SQL
От: _ABC_  
Дата: 08.09.15 15:51
Оценка: 149 (3)
Здравствуйте, Sinix, Вы писали:

S>Так DB_ID() тоже будет молчать как партизан в этом случае. В документации раздел Permissions откровенно скопипащщен с sys.databases.

Не будет. Тут дыра. Проверено на 2012 и 2014.

Учитывая, что для более ранних версий в документации по этому поводу вообще ничего нет,
то там поведение аналогично 2012+.
Re[3]: [SQL Server] EF, CreateIfNotExists и T-SQL
От: Olaf Россия  
Дата: 08.09.15 19:34
Оценка:
Здравствуйте, _ABC_, Вы писали:

_AB>Она не работала.


_AB>Если у логина нет прав на view any database, то

_AB>
_AB>SELECT Count(*) FROM sys.databases WHERE [name]=N'Test'
_AB>

_AB>выдаст 0. Создание БД тем не менее не, разумеется, не сработает.

Опять же это еще один вопрос особенностей реализации проверки до версии 6.0 в EF Потому что даже при отсутствии прав на уровне сервера deny view any database to user проверить наличие базы данных можно в случае, если она является текущей у пользователя. А производитель EF по всей видимости использует master, именно поэтому и возвращается 0.
Re[2]: [SQL Server] EF, CreateIfNotExists и T-SQL
От: Olaf Россия  
Дата: 08.09.15 19:44
Оценка:
Здравствуйте, IT, Вы писали:

IT>Ещё проще:


IT>
IT>SELECT db_id(N'Test')
IT>

Согласен, так будет проще, но может быть автор в новой версии попытался сохранить преемственность подхода к анализу результатов, т.е. при наличии БД возвращается 1, в противном случае 0. В вашем же варианте вернутся номер базы данных и null.
Re[4]: [SQL Server] EF, CreateIfNotExists и T-SQL
От: _ABC_  
Дата: 08.09.15 20:09
Оценка: 18 (1)
Здравствуйте, Olaf, Вы писали:

O>Опять же это еще один вопрос особенностей реализации проверки до версии 6.0 в EF Потому что даже при отсутствии прав на уровне сервера deny view any database to user проверить наличие базы данных можно в случае, если она является текущей у пользователя.

Не понял. А смысл проверять наличие БД, если контекст БД уже успешно установлен?
Кстати, проверить через sys.databases еще можно, если пользователь является database owner.

Тем не менее, в общем случае старый метод не работает. Думаю, что просто поступило описание бага
и они пофиксили бездумно, дописав в той же строке условие по db_id, как это было написано в
комментариях к багу.

O>А производитель EF по всей видимости использует master, именно поэтому и возвращается 0.

Производитель EF по всей видимости не убирает права view any database у public. И к тому же
обычно работает с БД, которые созданы тем же логином, под которым и работает приложение
(т.е. под логином database owner).

С другой стороны, в меру параноидальные DBA не позволяют приложениям самим создавать БД. А не параноидальные
не парятся с отбиранием прав у паблика. Тем более, что обходится это злоумышленниками на раз-два.

Может быть, поэтому такое поведение и дожило до 6-й версии. С одной стороны разрабы не особо парятся.
С другой DBA если и парятся, то так, что данная фича становится неприменимой.
Re[5]: [SQL Server] EF, CreateIfNotExists и T-SQL
От: Olaf Россия  
Дата: 09.09.15 04:03
Оценка:
Здравствуйте, _ABC_, Вы писали:

_AB>Здравствуйте, Olaf, Вы писали:


O>>Опять же это еще один вопрос особенностей реализации проверки до версии 6.0 в EF Потому что даже при отсутствии прав на уровне сервера deny view any database to user проверить наличие базы данных можно в случае, если она является текущей у пользователя.

_AB>Не понял. А смысл проверять наличие БД, если контекст БД уже успешно установлен?
_AB>Кстати, проверить через sys.databases еще можно, если пользователь является database owner.

_AB>Тем не менее, в общем случае старый метод не работает. Думаю, что просто поступило описание бага

_AB>и они пофиксили бездумно, дописав в той же строке условие по db_id, как это было написано в
_AB>комментариях к багу.

Спасибо, теперь всё встало на свои места. Вчера я сказал глупость, поздно было – ни о какой текущей базе речи идти не может, ведь ее еще нет на сервере, а проверка через sys.databases должна это выявить, поэтому только master.
Re[5]: Fixed
От: Olaf Россия  
Дата: 20.10.16 03:06
Оценка: 72 (2)
Здравствуйте, _ABC_, Вы писали:

_AB>Здравствуйте, Sinix, Вы писали:


S>>Так DB_ID() тоже будет молчать как партизан в этом случае. В документации раздел Permissions откровенно скопипащщен с sys.databases.

_AB>Не будет. Тут дыра. Проверено на 2012 и 2014.

_AB>Учитывая, что для более ранних версий в документации по этому поводу вообще ничего нет,

_AB>то там поведение аналогично 2012+.

Дыру закрыли. В версии 2016 возможно с момента релиза, а в 2014 совсем недавно, т.е. с момента выпуска CU#2 для SP2:

Теперь в отсутствии минимальных разрешений, для функций db_id() и db_name() возвращается null, а не как раньше ID и имя БД.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.