SQL CLR 在 SQL 服务器中随机失败
SQL CLR randomly fails in SQL Server
我创建了一个 .Net 程序集并将其导入 SQL 服务器并在标量值函数 (SVF) 中使用它。
当我执行 SVF 时,它工作正常。第二次也是。但是每三次 SVF 失败并且潜在的错误是
System.NullReferenceException: Object reference not set to an instance of an object
我已经向 CLR 添加了 try/catch 语句和调试数据,但我仍然不知道为什么这个 CLR 会失败。
你能帮帮我吗?
这是 C# 代码:
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read)]
public static SqlString HEAD(SqlString Uri)
{
string statusCode = "";
// Debug info
string debugData = "1,";
try
{
debugData += "2,";
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
debugData += "3,";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(Convert.ToString(Uri));
req.Method = "HEAD";
req.AllowAutoRedirect = true;
debugData += "4,";
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
debugData += "5,";
if (resp.StatusCode == HttpStatusCode.MethodNotAllowed)
{
// Use GET instead of HEAD
debugData += "6,";
req = (HttpWebRequest)WebRequest.Create(Convert.ToString(Uri));
req.Method = "GET";
resp = (HttpWebResponse)req.GetResponse();
}
debugData += "7,";
statusCode = ((int)resp.StatusCode).ToString();
debugData += "8,";
}
catch (WebException ex)
{
debugData += "9,";
HttpWebResponse webResponse = (HttpWebResponse)ex.Response;
debugData += "10,";
statusCode = ((int)webResponse.StatusCode).ToString();
debugData += "11,";
}
catch (Exception ex)
{
debugData += "12,";
statusCode = debugData + ex.Message;
debugData += "13,";
}
debugData += "14,";
//return (statusCode);
return debugData;
}
该方法将字符串作为输入(URL)并通过使用 HEAD 方法执行 HttpWebrequest 检查 URL 是否实际存在。
它 returns 状态代码或错误消息(因此字符串作为输出)。
程序集已成功导入我的数据库:
ALTER DATABASE MyDatabase SET TRUSTWORTHY ON;
GO
CREATE ASSEMBLY SQLHttpRequest
FROM N'C:\somefolder\SQLHttpRequest.dll'
WITH PERMISSION_SET=UNSAFE;
GO
这是 SVF:
CREATE FUNCTION dbo.uf_head_webrequest
(@Uri nvarchar(max))
RETURNS nvarchar(max)
AS
EXTERNAL NAME SQLHttpRequest.[SQLHttpRequest.HTTPFunctions].HEAD;
我是这样测试这个功能的:
declare @uri nvarchar(1000) = 'https://logos-download.com/wp-content/uploads/2016/02/Microsoft_logo_SQL-700x566.png'
SELECT dbo.uf_head_webrequest(@uri)
输出为1,2,3,4,5,7,8,14,
但是,如果我执行几次,我会得到这样的响应:
Msg 6522, Level 16, State 1, Line 2
A .NET Framework error occurred during the execution of user-defined routine or aggregate "uf_head_webrequest":
System.NullReferenceException: Object reference not set to an instance of an object.
System.NullReferenceException:
at SQLHttpRequest.HTTPFunctions.HEAD(SqlString Uri)
.
我理解对象实例化的概念,但不明白为什么我没有得到任何调试数据。
我在网上搜索了一下,然后在 SO 上提出了这个问题:
这描述了 NULL 值的问题。但在我看来,这里并非如此。特别是因为我一次又一次地使用相同的 Uri。
谢谢!
可能是这一行(在 catch (WebException ex)
中):
HttpWebResponse webResponse = (HttpWebResponse)ex.Response;
您假设 ex.Response
存在。首先尝试检查它是否为 null
。这可以解释为什么您没有取回 debugData
值(由于错误处理中出现未处理的异常)。
但是,即使修复了错误,这段代码仍然存在几个问题,最大的问题是:
- 不清理一次性资源(即没有
using()
)
- 使用
TRUSTWORTHY ON
(请参阅:PLEASE, Please, please Stop Using Impersonation, TRUSTWORTHY, and Cross-DB Ownership Chaining)
- 不必要地将程序集设置为
UNSAFE
而不是 EXTERNAL_ACCESS
小问题:
- 设置
DataAccessKind = Read
(您没有进行数据访问,这是对性能的影响;可能不会增加此类操作的性能,但仍然如此)
- 使用
Convert.ToString(Uri)
而不仅仅是 Uri.Value
最后,您为什么要通过编写自己的 HTTP 请求来重新发明轮子?这个确切功能有几个现有选项,它们功能更强大,并且已经过一些测试。我在 GitHub 上看到了一两个不错的免费选项(不要只是从一些博客上获取源代码,因为那里有几个相当糟糕的例子),还有一个非免费选项来自我的 SQL# (SQLsharp) 项目(虽然有免费版本,但 INET_GetWebPages 功能仅在付费版本中),我相信这些免费选项中有一些功能不可用(在至少在我上次检查时没有。
我创建了一个 .Net 程序集并将其导入 SQL 服务器并在标量值函数 (SVF) 中使用它。
当我执行 SVF 时,它工作正常。第二次也是。但是每三次 SVF 失败并且潜在的错误是
System.NullReferenceException: Object reference not set to an instance of an object
我已经向 CLR 添加了 try/catch 语句和调试数据,但我仍然不知道为什么这个 CLR 会失败。
你能帮帮我吗?
这是 C# 代码:
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read)]
public static SqlString HEAD(SqlString Uri)
{
string statusCode = "";
// Debug info
string debugData = "1,";
try
{
debugData += "2,";
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
debugData += "3,";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(Convert.ToString(Uri));
req.Method = "HEAD";
req.AllowAutoRedirect = true;
debugData += "4,";
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
debugData += "5,";
if (resp.StatusCode == HttpStatusCode.MethodNotAllowed)
{
// Use GET instead of HEAD
debugData += "6,";
req = (HttpWebRequest)WebRequest.Create(Convert.ToString(Uri));
req.Method = "GET";
resp = (HttpWebResponse)req.GetResponse();
}
debugData += "7,";
statusCode = ((int)resp.StatusCode).ToString();
debugData += "8,";
}
catch (WebException ex)
{
debugData += "9,";
HttpWebResponse webResponse = (HttpWebResponse)ex.Response;
debugData += "10,";
statusCode = ((int)webResponse.StatusCode).ToString();
debugData += "11,";
}
catch (Exception ex)
{
debugData += "12,";
statusCode = debugData + ex.Message;
debugData += "13,";
}
debugData += "14,";
//return (statusCode);
return debugData;
}
该方法将字符串作为输入(URL)并通过使用 HEAD 方法执行 HttpWebrequest 检查 URL 是否实际存在。
它 returns 状态代码或错误消息(因此字符串作为输出)。
程序集已成功导入我的数据库:
ALTER DATABASE MyDatabase SET TRUSTWORTHY ON;
GO
CREATE ASSEMBLY SQLHttpRequest
FROM N'C:\somefolder\SQLHttpRequest.dll'
WITH PERMISSION_SET=UNSAFE;
GO
这是 SVF:
CREATE FUNCTION dbo.uf_head_webrequest
(@Uri nvarchar(max))
RETURNS nvarchar(max)
AS
EXTERNAL NAME SQLHttpRequest.[SQLHttpRequest.HTTPFunctions].HEAD;
我是这样测试这个功能的:
declare @uri nvarchar(1000) = 'https://logos-download.com/wp-content/uploads/2016/02/Microsoft_logo_SQL-700x566.png'
SELECT dbo.uf_head_webrequest(@uri)
输出为1,2,3,4,5,7,8,14,
但是,如果我执行几次,我会得到这样的响应:
Msg 6522, Level 16, State 1, Line 2
A .NET Framework error occurred during the execution of user-defined routine or aggregate "uf_head_webrequest":
System.NullReferenceException: Object reference not set to an instance of an object.
System.NullReferenceException:
at SQLHttpRequest.HTTPFunctions.HEAD(SqlString Uri)
.
我理解对象实例化的概念,但不明白为什么我没有得到任何调试数据。
我在网上搜索了一下,然后在 SO 上提出了这个问题:
这描述了 NULL 值的问题。但在我看来,这里并非如此。特别是因为我一次又一次地使用相同的 Uri。
谢谢!
可能是这一行(在 catch (WebException ex)
中):
HttpWebResponse webResponse = (HttpWebResponse)ex.Response;
您假设 ex.Response
存在。首先尝试检查它是否为 null
。这可以解释为什么您没有取回 debugData
值(由于错误处理中出现未处理的异常)。
但是,即使修复了错误,这段代码仍然存在几个问题,最大的问题是:
- 不清理一次性资源(即没有
using()
) - 使用
TRUSTWORTHY ON
(请参阅:PLEASE, Please, please Stop Using Impersonation, TRUSTWORTHY, and Cross-DB Ownership Chaining) - 不必要地将程序集设置为
UNSAFE
而不是EXTERNAL_ACCESS
小问题:
- 设置
DataAccessKind = Read
(您没有进行数据访问,这是对性能的影响;可能不会增加此类操作的性能,但仍然如此) - 使用
Convert.ToString(Uri)
而不仅仅是Uri.Value
最后,您为什么要通过编写自己的 HTTP 请求来重新发明轮子?这个确切功能有几个现有选项,它们功能更强大,并且已经过一些测试。我在 GitHub 上看到了一两个不错的免费选项(不要只是从一些博客上获取源代码,因为那里有几个相当糟糕的例子),还有一个非免费选项来自我的 SQL# (SQLsharp) 项目(虽然有免费版本,但 INET_GetWebPages 功能仅在付费版本中),我相信这些免费选项中有一些功能不可用(在至少在我上次检查时没有。