【问题标题】:CLR function will not return more than 4000 charsCLR 函数不会返回超过 4000 个字符
【发布时间】:2018-10-14 08:21:10
【问题描述】:

第一次发帖,请多指教! :-) VB/C# 的新手,尝试修复调用 Web 服务的 CLR 函数。我拼凑了以下内容:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Collections;
using System.Globalization;

// For the SQL Server integration
using Microsoft.SqlServer.Server;

// Other things we need for WebRequest
using System.Net;
using System.Text;
using System.IO;

public partial class UserDefinedFunctions
{

// Function to return a web URL as a string value.
[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read)]

public static SqlChars GET(SqlString uri, SqlString username, SqlString passwd)
{
    // The SqlPipe is how we send data back to the caller
    SqlPipe pipe = SqlContext.Pipe;
    SqlChars document;

    // Set up the request, including authentication
    WebRequest req = WebRequest.Create(Convert.ToString(uri));
    if (Convert.ToString(username) != null & Convert.ToString(username) != "")
    {
        req.Credentials = new NetworkCredential(
            Convert.ToString(username),
            Convert.ToString(passwd));
    }
    ((HttpWebRequest)req).UserAgent = "CLR web client on SQL Server";

    // Fire off the request and retrieve the response.
    // We'll put the response in the string variable "document".
    WebResponse resp = req.GetResponse();
    Stream dataStream = resp.GetResponseStream();
    StreamReader rdr = new StreamReader(dataStream);
    document = new SqlChars(rdr.ReadToEnd());
    if (document.IsNull)
    {
        return SqlChars.Null;
    }

    // Close up everything...
    rdr.Close();
    dataStream.Close();
    resp.Close();

    // .. and return the output to the caller.
    return (document);
}

即使它是为 sqlChars 编码的,它也只会返回 4000 个字符。在完整的库存拉取期间,API 调用可能会返回 14MB 以上。如果我理解正确 sqlChars 转到 varchar(max) (或 nvarchar(max)) ... 我在这里想念什么?如果有帮助的话,在 sql server 2005 上为 .Net 3.0 编译... 安迪

【问题讨论】:

标签: c# sql-server web-services sqlclr


【解决方案1】:

SqlCharsSqlString 都不一定与 NVARCHAR(MAX)NVARCHAR(4000) 绑定。在 .NET 中,stringNVARCHAR(MAX),因此SqlCharsSqlString 都可以映射到NVARCHAR(MAX)NVARCHAR(4000)。唯一确定它是MAX 还是4000(或从技术上讲是14000)是CREATE [PROCEDURE | FUNCTION | AGGREGATE] 语句中为输入参数、返回类型或结果集列声明的数据类型。因此,您首先需要检查那里。它被明确定义为NVARCHAR(4000)。如果您只是将它(即ALTER FUNCTION)更改为NVARCHAR(MAX),它将返回所有内容。

其他一些注意事项:

  1. SQLCLR API 只允许NVARCHAR,而不是VARCHAR
  2. 如果您使用的是 Visual Studio / SSDT,您可以修饰您的方法,为 SSDT DDL 生成过程提供提示,告诉它使用NVARCHAR(MAX)。您可以通过在 [SqlFunction()] 装饰器上方添加以下内容来做到这一点:

    [return: SqlFacet(MaxSize = -1)]
    

    如果您希望返回类型为NVARCHAR(250),您可以使用MaxSize = 250

  3. 在 SQLCLR 中以这种方式使用外部资源时,您需要非常小心。 AppDomain 可以长时间保持加载并由所有 Session 共享。因此,如果您有一个异常并且没有 using() 构造并且没有在打开的资源上调用 Dispose()try ... finally,那么您可能会长时间孤立这些句柄。这可能会导致文件锁定(如果它是文件系统操作)或可能会用完网络套接字。您需要非常彻底地进行错误处理和资源清理!非常!我这里还有一些其他的答案和附加说明,例如:SQL CLR Web Service Call: Limiting Overhead

    这意味着,在当前形式下,问题中显示的代码是非常风险的,因为没有使用using() 构造或try...catch...finally事实上,通过返回return SqlChars.Null; 3 个.Close() 语句之前,您已经做了一件非常糟糕的事情。如果document.IsNull 曾经返回true,那么此代码将孤立该网络连接和那些外部资源!!!!!!

  4. 如果此 UDF 一次只能被一个会话/进程调用,并且永远不会被多个,那么您就可以了。否则,您将遇到默认外部连接数为 2 的问题,这会导致其他连接等到两个允许的连接之一关闭。在这种情况下,您需要在 ServicePointManager 类中设置默认连接数。
  5. 没有理由像你一样打电话给Convert.ToString({input_parameter})。所有Sql* 类型都有一个Value 属性,该属性以预期的.NET 类型返回传入的值。因此,您只需使用uri.Valueusername.Value 等即可。
  6. 没有理由指定此方法属性属性:

    DataAccess = DataAccessKind.Read
    

    只有在连接到数据库并访问数据时才指定Read。此代码中没有SqlConnection,因此您不需要这个会影响性能的属性。同样,您也不需要using System.Data.SqlClient;

  7. 有关使用 SQLCLR 的更多信息,请参阅我在 SQLCLR Info 上发布的资源列表
  8. 对于任何对 WebRequest SQLCLR UDF 的功能齐全、预先完成的实现感兴趣的人,SQL# 库(我编写的)中已经存在这样的函数。该函数名为 INET_GetWebPages,支持传入所有(或至少大多数)HTTP 标头,包括自定义标头、发送 POST 数据、以VARBINARY 形式返回二进制数据,覆盖每个 URI 最大值的默认 2 个连接等。请注意,虽然大多数功能在免费版本中可用,但此特定功能仅在完整(即付费)版本中可用。

【讨论】:

  • 我也认为函数是问题,但这里是代码: CREATE FUNCTION dbo.fn_get_webrequest( uri nvarchar(max), user nvarchar(255)=NULL, passwd nvarchar(255)=NULL )返回 nvarchar(max) 作为外部名称 SqlWebRequest.UserDefinedFunctions.GET;去吧,所以我不知道该怎么做。
  • @A.Guattery 您是否将函数结果与某些东西连接起来?
  • @A.Guattery 虽然我不认为将返回值与任何东西连接会导致这种情况,但 GSerg 确实提出了一个很好的观点,即我们需要准确地看到 你是如何测试这个的。请将CREATE FUNCTION T-SQL 发布到问题以及您的测试查询中。您是否将返回值存储在 NVARCHAR(4000) 变量中?你怎么知道它只返回 4000 个字符?你怎么知道远程服务返回了4000多个?
  • @A.Guattery 另外,请参阅我的更新答案。我添加了一些额外的注释,包括我刚刚注意到在您的代码中非常危险的内容。请在纠正缺陷之前不要使用此代码(注释 #3 的粗体部分)!
  • @SolomonRutzky ...找到了!返回变量最初被声明为 nvarchar(max),但进一步进入代码被重新声明为 nvarchar(4000)。所以你的洞察力解决了最初的问题。我在中途继承了这个烂摊子,即使我的知识有限,我也在尽我最大的努力让这个 CLR 工作。我已经删除了 NULL 返回;即使我知道那很糟糕。我在这里动态学习,但在我看来,它就像 C# try...catch...finally 与 t-sql 足够相似,我可以正确地将其放入其中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-05
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多