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 值(由于错误处理中出现未处理的异常)。

但是,即使修复了错误,这段代码仍然存在几个问题,最大的问题是:

  1. 不清理一次性资源(即没有using()
  2. 使用TRUSTWORTHY ON(请参阅:PLEASE, Please, please Stop Using Impersonation, TRUSTWORTHY, and Cross-DB Ownership Chaining
  3. 不必要地将程序集设置为 UNSAFE 而不是 EXTERNAL_ACCESS

小问题:

  1. 设置 DataAccessKind = Read(您没有进行数据访问,这是对性能的影响;可能不会增加此类操作的性能,但仍然如此)
  2. 使用 Convert.ToString(Uri) 而不仅仅是 Uri.Value

最后,您为什么要通过编写自己的 HTTP 请求来重新发明轮子?这个确切功能有几个现有选项,它们功能更强大,并且已经过一些测试。我在 GitHub 上看到了一两个不错的免费选项(不要只是从一些博客上获取源代码,因为那里有几个相当糟糕的例子),还有一个非免费选项来自我的 SQL# (SQLsharp) 项目(虽然有免费版本,但 INET_GetWebPages 功能仅在付费版本中),我相信这些免费选项中有一些功能不可用(在至少在我上次检查时没有。