Каков рекомендуемый способ добавления хранимой процедуры SQL Server в модель Entity Framework
Резюме
В нашем приложении мы используем Entity Framework 6 и SQL Server 2016. При импорте в модель EF хранимую процедуру со сложной логикой, в частности при использовании временных таблиц, мы помещаем поверх:
SET FMTONLY OFF
Это заставляет хранимую процедуру выполнять полную логику и, следовательно, возвращать правильный выходной формат, что позволяет модели EF правильно генерировать сложный тип.
В целом работает нормально. Однако в документации Microsoft https://docs.microsoft.com/en-us/sql/t-sql/statements/set-fmtonly-transact-sql?view=sql-server-ver15 указано: Не используйте эту функцию. Поэтому мне интересно, какой рекомендуемый способ - импортировать хранимую процедуру и правильно сгенерировать сложный тип.
Подробнее
Во-первых, меня смущает эта заметка. Не используйте эту функцию на веб-сайте Microsoft о
FMTONLY
. Разве это не та же команда (с опцией
ON
) вызывается программой обновления модели EF? Это означает, что средство обновления модели EF использует функцию, которую не следует использовать.
Во-вторых, я предполагаю, что это поведение средства обновления модели EF направлено на предотвращение нежелательных манипуляций с данными БД, когда хранимая процедура выполняется со всеми входными аргументами, равными
NULL
. Но реальное поведение не соответствовало этому предположению.
Рассмотрим простую хранимую процедуру ниже, где я пытался избежать использования
SET FMTONLY OFF
. Я проанализировал точное поведение с помощью профилировщика SQL при обновлении модели EF из Visual Studio (2013, CU 5, Pro).
SET NOCOUNT ON;
IF (1=0) --FOR EF edmx update this will actually execute
BEGIN
DECLARE @tempTable TABLE
(
[id] [int],
[date_local] [datetime2](7),
[value] [float] NULL
)
SELECT * FROM @tempTable
RETURN 0
END
CREATE TABLE #TempValue
(
[id] [int] NOT NULL,
[date_local] [datetime2](7) NOT NULL,
[value] [float] NULL
CONSTRAINT [PK_TempValue]
PRIMARY KEY CLUSTERED ([id] ASC, [date_local] ASC)
WITH (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO #TempValue (id, date_local, value)
SELECT @id, *
FROM OPENJSON(@valueJson)
WITH (
date_local datetime2(7) '$.dateTime',
value float '$.value'
);
SELECT * FROM #TempValue
Точки:
Программа обновления модели EF называет это, входит внутрь
IF (1=0)
(как и ожидалось сSET FMTONLY ON
) и работаетSELECT * from @tempTable
правильно.Игнорирует
return 0
и продолжает выполнение остальной части тела хранимой процедуры.Он пропускает
CREATE TABLE #TempValue
но потом пытается бежатьINSERT INTO #TempValue
который, конечно, не существует, поэтому он останавливается здесь и не выполняетSELECT * FROM #TempValue
, который также потерпит неудачу.
В этом случае сложный тип был сгенерирован правильно, потому что последний успешный выбор был
SELECT * from @tempTable
. Но я считаю, что это просто удача, потому что нет никаких последующих выборок, которые действительно выполняются. Для меня все это поведение ошибочно, в частности пропуск
return 0
и
CREATE TABLE #TempValue
но затем позволяя
INSERT INTO
; если вы позволите
INSERT INTO
неужели вы не сохраняете данные БД?
Итак, мы попробовали два обходных пути, которые на самом деле работают:
Положить
SET FMTONLY OFF
наверху. Это работает, но меня это немного беспокоит. Не используйте эту функцию на веб-сайте Microsoft.Закомментируйте все тело SP и добавьте
DECLARE @tempTable table ... SELECT * from @tempTable
как в примере выше. После обновления модели EF удалите добавленный фрагмент и раскомментируйте исходный код. Делайте это каждый раз при обновлении модели EF, так что это не очень эффективно.
Какой правильный и рекомендуемый способ сделать это?