SQL CLR:流表重要的函数结果

时间:2011-08-01 16:57:12

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

我的问题与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方法获取数据,或者是a   UDT验证方法。

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

  

从用户定义的表中获取新行时发生错误   有价值的功能:   System.Security.SecurityException:请求权限   输入'System.Data.SqlClient.SqlClientPermission,System.Data,   Version = 2.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089'   失败。

3 个答案:

答案 0 :(得分:5)

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

  

必须使用permission_set = external_access

创建程序集

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

  

程序集'assemblyName'的CREATE ASSEMBLY因组装而失败   'assemblyName'未获得PERMISSION_SET = EXTERNAL_ACCESS的授权。   如果满足以下任一条件,则授权组件:   数据库所有者(DBO)具有EXTERNAL ACCESS ASSEMBLY权限和   database上有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";

它有效!

答案 1 :(得分:3)

原始问题是由于在您的函数中使用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

答案 2 :(得分:1)

谷歌搜索:

  

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

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

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

private static ArrayList FlatFile(SqlChars Delimeter, SqlChars TextQualifier)

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

一旦我在新方法中删除了这些数据类型并使用了C#(string,bool,int),错误终于消失了。

private static ArrayList FlatFile(string Delimeter, string TextQualifier)

注意:当我使用 模拟 从其他域获取文件时,此出错。 当我通过本地域传输文件时,我没有收到此错误,这就是让我失望的原因。

我希望在您需要的时候帮助您。我为此排除了太多时间。