【问题标题】:SQL CLR: Streaming table valued function resultsSQL CLR:流表值函数结果
【发布时间】:2011-10-17 15:00:44
【问题描述】:

我的问题与this 问题非常相似。

但是,我使用的是 SQL Server 2005 Service Pack 2 (SP2) (v9.0.3042),而那里发布的解决方案对我不起作用。我尝试使用两个连接字符串。一个在我的代码中被注释掉了。

我意识到我可以将所有结果存储在内存中的 List 或 ArrayList 中并返回。我已经成功地做到了,但这不是这里的目标。目标是能够在结果可用时对其进行流式传输。

这可以使用我的 SQL Server 版本吗?

这是我的代码: (请注意,这些参数当前并未实际使用。我这样做是为了调试)

public static class StoredProcs
{
    [SqlFunction(
        DataAccess = DataAccessKind.Read,
        SystemDataAccess=SystemDataAccessKind.Read,
        FillRowMethodName="FillBaseline",
        TableDefinition = "[baseline_id] [int], [baseline_name] [nvarchar](256), [description] [nvarchar](max), [locked] [bit]"
        )]
    public static IEnumerable fnGetBaselineByID(SqlString projectName, SqlInt32 baselineID)
    {
        string connStr = "context connection=true";
        //string connStr = "data source=.;initial catalog=DBName;integrated security=SSPI;enlist=false";
        using (SqlConnection conn = new SqlConnection(connStr))
        {
            conn.Open();
            using (SqlCommand cmd = new SqlCommand(String.Format(@"
                SELECT *
                FROM [DBName].[dbo].[Baseline] WITH (NOLOCK)
            "), conn))
            {
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        yield return new Baseline(reader);
                    }
                }
            }
        };
    }

    public static void FillBaseline(Object obj, out SqlInt32 id, out SqlString name, out SqlString description, out bool locked)
    {
        Baseline baseline = (Baseline)obj;
        id = baseline.mID;
        name = baseline.nName;
        description = baseline.mDescription;
        locked = baseline.mLocked;
    }
}

这是我的 SQL 部署脚本的一部分:

CREATE ASSEMBLY [MyService_Stored_Procs]
FROM 'C:\temp\assemblyName.dll'
WITH PERMISSION_SET = SAFE

当我使用连接字符串“context connection=true”时,我收到此错误:

从用户定义的表中获取新行时出错 价值函数: System.InvalidOperationException:不允许数据访问 这个上下文。上下文是未标记的函数或方法 使用 DataAccessKind.Read 或 SystemDataAccessKind.Read,是一个回调 从表值函数的 FillRow 方法获取数据,或者是 UDT 验证方法。

当我使用其他连接字符串时,我收到此错误:

从用户定义的表中获取新行时出错 价值函数: System.Security.SecurityException:请求权限 键入'System.Data.SqlClient.SqlClientPermission,System.Data, 版本=2.0.0.0,文化=中性,PublicKeyToken=b77a5c561934e089' 失败了。

【问题讨论】:

    标签: c# sql clr user-defined-functions


    【解决方案1】:

    经过进一步研究和反复试验,我找到了解决方案。 article that I mentioned here

    您的程序集必须使用 permission_set=external_access 创建

    说起来容易做起来难,但这是一个很好的起点。只需使用该行代替 permission_set=safe 就会出现错误:

    为程序集“assemblyName”创建程序集失败,因为程序集 'assemblyName' 未获得 PERMISSION_SET = EXTERNAL_ACCESS 的授权。 当下列任何一项为真时,大会被授权: 数据库所有者 (DBO) 具有 EXTERNAL ACCESS ASSEMBLY 权限,并且 数据库具有 TRUSTWORTHY 数据库属性;或大会是 使用证书或非对称密钥签名,该密钥具有 具有 EXTERNAL ACCESS ASSEMBLY 权限的相应登录。

    所以我要做的第一件事就是签署我的 dll 文件。要在 Visual Studio 2010 中执行此操作,请转到项目属性的“签名”选项卡,然后选中“签署程序集”并为其命名。对于此示例,名称为 MyDllKey。我选择不使用密码保护它。然后,当然,我将dll文件复制到sql server:C:\Temp

    使用this page作为参考,我使用这3个命令基于上述密钥创建了一个SQL登录:

    CREATE ASYMMETRIC KEY MyDllKey FROM EXECUTABLE FILE = 'C:\Temp\MyDll.dll'
    CREATE LOGIN MyDllLogin FROM ASYMMETRIC KEY MyDllKey
    GRANT EXTERNAL ACCESS ASSEMBLY TO MyDllLogin
    

    如上创建登录后,我现在可以使用以下方法创建程序集:

    CREATE ASSEMBLY [MyDll]
    FROM 'C:\Temp\MyDll.dll'
    WITH PERMISSION_SET = EXTERNAL_ACCESS
    

    现在唯一要做的就是使用正确的连接字符串。显然无法将enlist=falseconnection=true 结合使用。这是我使用的连接字符串的示例。

    string connStr = @"data source=serverName\instanceName;initial catalog=DBName;integrated security=SSPI;enlist=false";
    

    而且它有效!

    【讨论】:

      【解决方案2】:

      最初的问题是由于在您的函数中使用了 yield 关键字,如以下问题所述:SqlFunction fails to open context connection despite DataAccessKind.Read present

      如果您避免使用 yield(将结果存储在中间数组中,最后返回整个批次),问题就会消失。

      或者您可以按照您的描述进行操作并避免使用上下文连接,但是如果您这样做,您必须按照您的描述将您的程序集标记为外部访问。我认为最好将其描述为一种解决方法,而不是解决方案,因为您输掉了some of the benefits available from a context connection 并且因为您必须跳过所有额外的箍。

      在许多情况下,能够使用流式行为(收益)的好处确实超过了这种痛苦,但仍然值得考虑这两种选择。

      这是 Connect 上的错误:http://connect.microsoft.com/SQLServer/feedback/details/442200/sql-server-2008-clr-tvf-data-access-limitations-break-existing-code

      【讨论】:

      • 天哪,我浪费了半天才找到这个,问题一直是yield
      【解决方案3】:

      谷歌搜索:

      在此上下文中不允许数据访问。要么上下文是 未用 DataAccessKind.Read 标记的函数或方法或 SystemDataAccessKind.Read,是从 FillRow 获取数据的回调 表值函数的方法,或者是 UDT 验证方法。

      将我带到此页面,但没有我需要的答案。
      我最终弄清楚了它是什么。
      在我的 CLR 函数中,我正在调用另一个方法并传入函数接收到的值。

      听起来无害,但我所做的是对我添加的方法的输入参数使用相同的数据类型(SqlChars、SqlBoolean、SqlInt32)。

      private static ArrayList FlatFile(SqlChars Delimeter, SqlChars TextQualifier)
      

      显然,将这​​些数据类型用于 CLR SqlFunction 或 SqlProcedure 之外的任何内容有时会给您这种类型的神秘错误。

      一旦我在我的新方法中删除了这些数据类型并使用了 C# 数据类型(字符串、布尔值、整数),错误终于消失了。

      private static ArrayList FlatFile(string Delimeter, string TextQualifier)
      

      注意:当我使用 Impersonation 从另一个域获取文件时,这出错了。
      当我通过本地域流式传输文件时,我没有收到此错误,这让我很反感。

      我希望这对您有需要时有所帮助。我花了太多时间来解决这个问题。

      【讨论】:

        猜你喜欢
        • 2017-04-29
        • 2011-08-04
        • 1970-01-01
        • 1970-01-01
        • 2016-01-02
        • 1970-01-01
        • 2011-06-24
        • 1970-01-01
        • 2011-06-12
        相关资源
        最近更新 更多