Проблема связана с SSDT (инструменты данных SQL Server), а не с SQLCLR. Да, SQLCLR действительно поддерживает DATETIME2
через DateTime?
/Nullable<DateTime>
. К сожалению, SSDT (я использую VS2013 и SSDT версии 12.0.50512.0) пока (знаю, я настроен оптимистично) не поддерживает вывод DATETIME2
из DateTime
или DateTime?
. Но также не возникает ошибок при использовании DateTime?
, как раньше. Тем не менее, либо DateTime
, либо DateTime?
будут отображаться как обычные DATETIME
в сгенерированном SQL.
Я не уверен в каких-либо действительно «подходящих» способах сообщить SSDT, каким должен быть тип данных. На самом деле существует довольно много неподдерживаемых параметров, включая общие параметры UDF, такие как WITH RETURNS NULL ON NULL INPUT
, и параметры параметров, такие как значения по умолчанию. Грустно и обидно, да.
Лучшее, что я придумал до сих пор (и я все еще ищу другие варианты), — это добавить скрипт Post Deployment в ALTER
определение функции с желаемыми параметрами:
- В меню ПРОЕКТ выберите Добавить новый элемент... (Control+Shift+A< /kbd> в VS2013)
- Перейдите в раздел SQL Server > Пользовательские сценарии.
- Выберите Сценарий после развертывания.
- Дайте ему имя (само имя не определяет, используется ли оно до развертывания, после развертывания или ни то, ни другое; оно просто должно заканчиваться на .sql) и нажмите Добавить кбд>
- Вы будете помещены в (в основном) пустой скрипт SQL
Введите одно или несколько утверждений ALTER
, подобных следующему:
-- declare once
DECLARE @ObjectName sysname; -- keep lower-case to work in case-sensitive collations
SET @ObjectName = N'Test';
IF (EXISTS(
SELECT *
FROM sys.assembly_modules sam
WHERE sam.[object_id] = OBJECT_ID(@ObjectName)
)
)
BEGIN
PRINT 'Checking custom properties for [' + @ObjectName + N']...';
IF (EXISTS(
SELECT *
FROM sys.parameters sp
INNER JOIN sys.types st
ON st.system_type_id = sp.system_type_id
WHERE sp.[object_id] = OBJECT_ID(@ObjectName)
AND st.[name] <> N'datetime2' -- keep lower-case to work in
-- case-sensitive collations
)
)
BEGIN
PRINT 'Setting custom properties for [' + @ObjectName + N']...';
BEGIN TRY
EXEC('
ALTER FUNCTION [dbo].[Test](@d [datetime2])
RETURNS [datetime2] WITH EXECUTE AS CALLER
AS EXTERNAL NAME [Test].[Test.UserDefinedFunctions].[Test];
');
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
SET @ErrorMessage = ERROR_MESSAGE();
RAISERROR(@ErrorMessage, 16, 1);
RETURN;
END CATCH;
END;
END;
ELSE
BEGIN
RAISERROR(N'Oops. [%s] was renamed or no longer exists!', 16, 1, @ObjectName);
RETURN;
END;
---
SET @ObjectName = N'NextObjectToFix';
-- copy the rest from above
Этот сценарий после развертывания будет всегда включаться в конец сценариев _Create
и публикации/добавочной сборки. Отсюда дополнительная логика, чтобы увидеть, присутствуют ли уже изменения или нет. Правда, обычно не помешает всегда запускать ALTER
, но в тех редких случаях, когда этот объект является зависимостью от чего-то другого, например, контрольного ограничения или вычисляемого столбца, вероятно, лучше просто оставьте его в покое, если он не нуждается в изменении.
Правильное определение ALTER
можно получить либо из сценария \bin\Configuration\*_Create.sql
(просто измените CREATE
на ALTER
), либо в SSMS, если щелкнуть объект правой кнопкой мыши и выбрать Изменить. В обоих случаях измените тип данных и любые другие параметры (очевидно ;-).
Другая, отчасти связанная идея состоит в том, чтобы просто не полагаться на процесс публикации Visual Studio/SSDT для операторов объекта-оболочки T-SQL CREATE
, а использовать его только для управления сборкой. В этой настройке вы должны снять флажок Создать DDL на вкладке SQLCLR в Свойствах проекта. Затем вы должны добавить сценарий после развертывания (как указано в предложении выше) и добавить свои собственные операторы CREATE FUNCTION ...
, CREATE PROCEDURE ...
и т. д.
Для первоначальной разработки это не так быстро и просто для введения нового объекта, как использование SSDT для создания этого DDL, но, учитывая, что новые объекты создаются не так уж часто и их сигнатуры меняются гораздо реже, управлять этим DDL самостоятельно действительно реально. не так уж и плох (и на самом деле он очень похож на процесс, который я использую для моей библиотеки SQL#, в которой более 250 объектов. С практической точки зрения, когда у вас есть по одному оператору CREATE
каждого типа объекта, вы можете просто скопировать и вставить их для новых объектов, изменить имена, параметры и т. д. Больше всего работы, которую когда-либо требует этот подход, — это создание нового TVF если он возвращает кучу полей, но на самом деле это не так уж много работы, учитывая, что вам все равно пришлось бы вводить то же самое в свойство TableDefinition
атрибута SqlFunction
, если бы вы использовали SSDT для управления созданием DDL.
person
Solomon Rutzky
schedule
18.08.2015