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

我的问题与这个问题非常相似。

但是,我正在使用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方法获取数据的回调,或者是UDTvalidation方法。

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

从用户定义的表值函数获取新行时发生错误:System.Security.SecurityException:请求类型’System.Data.SqlClient.SqlClientPermission,System.Data,Version = 2.0.0.0,Culture = neutral,PublicKeyToken的权限= b77a5c561934e089’失败了。

经过进一步的研究和反复试验,我找到了解决方案。 我在这里提到的文章说

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

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

程序集’assemblyName’的CREATE ASSEMBLY失败,因为程序集’assemblyName’未授权PERMISSION_SET = EXTERNAL_ACCESS。 如果满足以下任一条件,则授权程序集:数据库所有者(DBO)具有EXTERNAL ACCESS ASSEMBLY权限,并且数据库具有TRUSTWORTHY数据库属性; 或者使用具有EXTERNAL ACCESS ASSEMBLY权限的相应登录的证书或非对称密钥对程序集进行签名。

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

使用此页面作为参考,我使用以下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=false并结合connection=true是不可能的。 这是我使用的连接字符串的示例。

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

它的工作原理!

原始问题是由于在函数中使用yield关键字,如此问题中所述: 尽管DataAccessKind.Read存在,但SqlFunction无法打开上下文连接 。

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

或者,您可以按照描述进行操作并避免使用上下文连接,但如果这样做,则必须按照描述标记程序集以进行外部访问。 我认为最好将其描述为一种解决方法,而不是解决方案,因为您失去了上下文连接中可用的一些好处,并且因为您必须跳过所有额外的环节。

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

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

谷歌搜索:

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

带我到这个页面,但没有我需要的答案。
我终于弄清楚它是什么。
在我的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) 

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

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