我试图从函数返回结果集,该函数使用Execute语句执行命令字符串。
create function GetLogApiCalls
(
@displayStart int,
@displayLength int,
@searchString nvarchar(1000),
@orderBy nvarchar(100),
@orderByDirection varchar(50)
)
returns @logs table(
[Id] INT NOT NULL,
[Provider] NVARCHAR (10) NULL,
[RequestIdentifier] UNIQUEIDENTIFIER NULL,
[RequestData] NVARCHAR (MAX) NULL,
[ResponseData] NVARCHAR (MAX) NULL,
[UserName] NVARCHAR (50) NULL,
[AccountName] NVARCHAR (100) NULL,
[AccountId] INT NULL,
[CreatedUserId] INT NULL,
[CreatedDate] DATETIME NULL,
[MethodName] NVARCHAR (100) NULL
)
AS
BEGIN
DECLARE @sqlScript NVARCHAR(1000)
IF(@searchString IS NOT NULL AND LEN(@searchString) > 0)
BEGIN
SET @sqlScript = N'SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY '+@orderBy+' '+@orderByDirection+') AS rn , * from LogAPICall where Provider like ''%'+@searchString+'%'' or MethodName like ''%'+@searchString+'%''
) as result where result.rn between '+ CONVERT(varchar,@displayStart) + ' and ' + CONVERT(varchar,@displayLength)
END
ELSE
BEGIN
SET @sqlScript = N'SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY '+@orderBy+' '+@orderByDirection+') AS RN , * FROM LogAPICall
) AS result WHERE result.rn between ' + CONVERT(varchar,@displayStart) + ' and ' + CONVERT(varchar,@displayLength)
END
insert @logs
Execute(@sqlScript)
return
END
当我运行此代码时,它会返回以下错误:
Invalid use of a side-effecting operator 'INSERT EXEC' within a function.
请帮我解决此问题。 感谢
答案 0 :(得分:5)
抱歉,您无法在函数期间执行动态SQL。您可以重新编写函数以不需要动态SQL,但这是否真的解决了潜在的问题?当你需要一个程序并且仍然被这个完全不相关的涉及返回类型的实体框架问题手工铐时,你会做什么?显然,您配置EF或连接程序的方式会破坏某些内容,否则 很多 的人会抱怨EF无法使用返回结果的程序集。你觉得这可能吗?
CREATE FUNCTION dbo.GetLogApiCalls -- dbo prefix, always
(
@displayStart INT,
@displayLength INT, -- is this a page size, like 20, or @displayEnd?
@searchString NVARCHAR(1000),
@orderBy NVARCHAR(100),
@orderByDirection VARCHAR(4)
)
RETURNS TABLE
AS -- make it an inline TVF. Multi-statement TVFs can be a perf nightmare.
RETURN
(
SELECT * FROM
(
SELECT rn = ROW_NUMBER() OVER (ORDER BY
CASE @orderByDirection WHEN 'ASC' THEN
CASE @orderBy WHEN N'Provider' THEN Provider
WHEN N'RequestData' THEN RequestData
WHEN N'ResponseData' THEN ResponseData
WHEN N'UserName' THEN UserName
WHEN N'AccountName' THEN AccountName
WHEN N'MethodName' THEN MethodName
WHEN N'RequestIdentifier'
THEN CONVERT(CHAR(36), RequestIdentifier)
WHEN N'CreatedDate' THEN CONVERT(CHAR(23), CreatedDate, 126)
END
END,
CASE @orderByDirection WHEN 'ASC' THEN
CASE @orderBy WHEN N'Id' THEN Id
WHEN N'AccountId' THEN AccountId
WHEN N'CreatedUserId' THEN CreatedUserId
END
END,
CASE @orderByDirection WHEN 'DESC' THEN
CASE @orderBy WHEN N'Provider' THEN Provider
WHEN N'RequestData' THEN RequestData
WHEN N'ResponseData' THEN ResponseData
WHEN N'UserName' THEN UserName
WHEN N'AccountName' THEN AccountName
WHEN N'MethodName' THEN MethodName
WHEN N'RequestIdentifier'
THEN CONVERT(CHAR(36), RequestIdentifier)
WHEN N'CreatedDate' THEN CONVERT(CHAR(23), CreatedDate, 126)
END
END DESC,
CASE @orderByDirection WHEN 'DESC' THEN
CASE @orderBy WHEN N'Id' THEN Id
WHEN N'AccountId' THEN AccountId
WHEN N'CreatedUserId' THEN CreatedUserId
END
END DESC), [Id],[Provider],[RequestIdentifier],[RequestData],
[ResponseData],[UserName],[AccountName],[AccountId],
[CreatedUserId],[CreatedDate],[MethodName]
FROM dbo.LogAPICall
WHERE LEN(@searchString) = 0 OR
(
@searchString > '' AND
(
Provider LIKE '%' + @searchString + '%'
OR MethodName LIKE '%' + @searchString + '%'
)
)
) AS x
WHERE rn BETWEEN @displayStart AND @displayStart + @displayLength - 1 -- assumption
);
这种动态ORDER BY
是魔鬼。即使作为内联TVF,这也只能通过点击并在任何引用查询中添加OPTION (RECOMPILE)
来表现不同的参数。解?使用带有动态SQL的存储过程,并分别找出您的Entity Framework配置问题。作为存储过程,这将更好:
CREATE PROCEDURE dbo.GetLogApiCalls -- dbo prefix, always
@displayStart INT,
@displayLength INT, -- is this a page size, like 20, or @displayEnd?
@searchString NVARCHAR(1000),
@orderBy NVARCHAR(100),
@orderByDirection VARCHAR(4)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX) = N'SELECT * FROM (
SELECT rn = ROW_NUMBER() OVER (ORDER BY '
+ @orderBy + ' ' + @orderByDirection + '),
[Id],[Provider],[RequestIdentifier],[RequestData],
[ResponseData],[UserName],[AccountName],[AccountId],
[CreatedUserId],[CreatedDate],[MethodName]
FROM dbo.LogAPICall'
+ CASE WHEN LEN(@searchString) > 0 THEN
' WHERE Provider LIKE ''%'' + @searchString + ''%''
OR MethodName LIKE ''%'' + @searchString + ''%'''
ELSE '' END
+ ') AS x
WHERE rn BETWEEN @displayStart
AND @displayStart + @displayLength - 1;';
DECLARE @params NVARCHAR(MAX) = N'@searchString NVARCHAR(1000),'
+ '@displayStart INT, @displayLength INT';
EXEC sp_executesql @sql, @params, @searchString, @displayStart, @displayLength;
END
GO
特别是如果您启用了设置optimize for ad hoc workloads
。现在,由于动态ORDER BY无法进行参数化,因此仍然容易进行SQL注入,因此您可能希望添加这些参数仅包含有效值的验证。