Msg 6522,执行clr存储过程期间的16级警告

时间:2015-07-23 09:07:44

标签: c# sql-server sql-server-2012 sqlclr clrstoredprocedure

我想创建SQL Server CLR存储过程,以便在SQL Server 2012中的表中插入一些行。

这是我的c#代码:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void InsertingRows ()
    {
        // Put your code here
        Random rnd = new Random();

        List<int> listtelnumber = new List<int>(new int[] { 1525407, 5423986, 1245398, 32657891, 123658974, 7896534, 12354698 });
        List<string> listfirstname = new List<string>(new string[] { "Babak", "Carolin", "Martin", "Marie", "Susane", "Michail", "Ramona", "Ulf", "Dirk", "Sebastian" });
        List<string> listlastname = new List<string>(new string[] { "Bastan", "Krause", "Rosner", "Gartenmeister", "Rentsch", "Benn", "Kycik", "Leuoth", "Kamkar", "Kolaee" });
        List<string> listadres = new List<string>(new string[] { "Deutschlan Chemnitz Sonnenstraße 59", "",
            "Deutschland Chemnitz Arthur-Strobel straße 124", " Deutschland Chemnitz Brückenstraße 3",
            "Iran Shiraz Chamran Blvd, Niayesh straße Nr.155", "",
            "Deutschland Berlin Charlotenburg Pudbulesky Alleee 52", "United State of America Washington DC. Farbod Alle",
            "" });

            using (SqlConnection conn = new SqlConnection("Data Source=WIN2012SERVER02;Initial Catalog=test;Persist Security Info=True;User ID=di_test;Password=di_test"))
            {
                SqlCommand insertcommand = new SqlCommand();
                SqlParameter firstname = new SqlParameter("@fname", SqlDbType.VarChar);
                SqlParameter lastname = new SqlParameter("@lname", SqlDbType.VarChar);
                SqlParameter tel = new SqlParameter("@tel", SqlDbType.Int);
                SqlParameter adres = new SqlParameter("@adres", SqlDbType.NVarChar);
                conn.Open();
            for (int i = 0; i < 10000; i++)
            {
                int tn = rnd.Next(0, 6);
                int fn = rnd.Next(0, 9);
                int ln = rnd.Next(0, 9);
                int an = rnd.Next(0, 9);

                firstname.Value = listfirstname[fn];
                lastname.Value = listlastname[ln];
                tel.Value = listtelnumber[tn];
                adres.Value = listadres[an];

                insertcommand.Parameters.Add(firstname);
                insertcommand.Parameters.Add(lastname);
                insertcommand.Parameters.Add(tel);
                insertcommand.Parameters.Add(adres);

                insertcommand.CommandText = "INSERT dbo.Unsprstb(Firstname,Lastname,Tel,adress) VALUES(@fname,@lname,@tel,@adres)";
                insertcommand.Connection = conn;

                insertcommand.ExecuteNonQuery();

            }
            conn.Close();
        }
    }
}

我可以在SQL Server中成功构建,部署和发布我的代码,但如果我在SQL Server中运行此CLR存储过程,我会看到以下消息:

  

Msg 6522,Level 16,State 1,procedure InsertingRows,Line 0
  执行用户定义的例程或聚合'InsertingRows'时发生.NET Framework错误:
  System.Security.SecurityException:类型为“System.Data.SqlClient.SqlClientPermission,System.Data,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089”的权限的错误请求。
  System.Security.SecurityException:
  bei System.Security.CodeAccessSecurityEngine.Check(Object demand,StackCrawlMark&amp; stackMark,Boolean isPermSet)
  bei System.Security.PermissionSet.Demand()
  bei System.Data.Common.DbConnectionOptions.DemandPermission()
  bei System.Data.SqlClient.SqlConnectionFactory.PermissionDemand(DbConnection outerConnection)
  bei System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection,DbConnectionFactory connectionFactory,TaskCompletionSource 1 retry, DbConnectionOptions userOptions)
bei System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource
1重试)
  bei System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1重试)
  bei System.Data.SqlClient.SqlConnection.Open()
  bei StoredProcedures.InsertingRows()

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:4)

此代码中存在一些需要解决的问题:

  1. 关于所述问题,当您收到 System.Security.SecurityException 错误时,该错误指的是尝试到达数据库外部的代码,这是{ {1}}装配。你如何解决这个问题取决于你想要完成的任务。

    • 如果您尝试访问文件系统,请从注册表中读取,获取环境变量,访问网络以获取非SQL Server连接(例如http,ftp)等,然后程序集需要{{1 } SAFE。要将程序集设置为PERMISSION_SET以外的任何其他值,您需要:
      • 根据您用于对程序集进行签名的相同密钥创建证书或非对称密钥(即赋予其强名称),根据该证书或非对称密钥创建登录,然后授予EXTERNAL_ACCESS该登录的权限。与其他方法相比,此方法非常首选:
      • 将包含程序集的数据库设置为SAFE。如果无法对程序集进行签名,则此方法只能用作最后的手段。或者用于快速测试目的。将数据库设置为EXTERNAL ACCESS ASSEMBLY会使您的实例面临潜在的安全威胁,应该避免,即使比其他方法更快/更容易。
    • 如果您尝试访问已登录的SQL Server实例,则可以选择使用TRUSTWORTHY ON的进程内连接,这可以在{{1}中完成}} 部件。这就是@Marc在他的回答中建议的。虽然使用这种类型的连接肯定有好处,虽然在这种特定情况下Context Connection是合适的选择,但是它过于简单和不正确地声明你总是使用这种类型的连接。让我们来看看上下文连接的正面和负面方面:

      • 肯定:
        • 可以在TRUSTWORTHY ON程序集中完成。
        • 连接开销非常低(如果有的话),因为它不是一个额外的连接。
        • 是当前会话的一部分,因此您执行的任何SQL都可以访问基于会话的项目,例如本地临时表和Context Connection = true;
      • 否定:

        • 如果已启用模拟,则无法使用。
        • 只能连接到当前的SQL Server实例。
        • 在函数(标量和表值)中使用时,它具有T-SQL函数具有的所有相同限制(例如,不允许任何副作用操作),除了您可以执行只读存储过程。
        • 表值函数如果读取结果集,则不允许将结果传回。

        所有这些&#34;否定&#34;在使用常规/外部连接时允许使用,即使它是从您执行此代码的同一实例。

  2. 如果要连接到正在执行此代码的实例并使用外部/常规连接,则无需指定服务器名称甚至使用SAFE。首选语法是SAFE,它使用共享内存,而其他语法有时可能使用效率不高的TCP / IP。

  3. 除非您有非常具体的理由,否则请勿使用CONTEXT_INFO

  4. localhost

  5. Server = (local)是一种很好的做法
  6. Persist Security Info=True;循环之前调用Dispose()更有效,然后在循环内部,只需通过SqlCommand设置值,您已经在这样做,只需将insertcommand.Parameters.Add()行移到for行之前。

  7. firstname.Value = / insertcommand.Parameters.Add() / fortel而不是@tel / listtelnumber。电话号码,就像邮政编码和社会安全号码(SSN)一样,不是号码,即使它们看起来像是。 INT无法存储前导VARCHAR或类似string的内容来表示&#34;扩展名&#34;。

  8. 所有这一切,即使以上所有内容都得到了纠正,这个代码仍然存在一个应该解决的问题:这是一个相当简单的操作在直接的T-SQL中,在SQLCLR中执行此操作过于复杂,维护起来更难,成本更高,而且速度更慢。此代码执行10,000个单独的事务,而它可以很容易地作为单个基于集的查询(即一个事务)完成。您可以将INT循环包装在一个可以加快它的事务中,但它仍然总是比基于集合的T-SQL方法慢,因为它仍然需要发出10,000个单独的0语句。您可以使用SQL Server 2008中引入的ex.CRYPT_GEN_RANDOM轻松地在T-SQL中随机化。(请参阅下面的更新部分)

    < / LI>

    如果您想了解有关SQLCLR的更多信息,请查看我为SQL Server Central撰写的系列文章:Stairway to SQLCLR(需要免费注册)。

    <强>更新

    这是使用问题中的值生成此随机数据的纯T-SQL方法。很容易为4个表变量中的任何一个添加新值(以增加可能的组合数),因为查询动态调整随机化范围以适应每个表变量中的任何数据(即行1 - n)。

    for

    注意:

    • 需要INSERT而不是NEWID()来获取整个DECLARE @TelNumber TABLE (TelNumberID INT NOT NULL IDENTITY(1, 1), Num VARCHAR(30) NOT NULL); INSERT INTO @TelNumber (Num) VALUES ('1525407'), ('5423986'), ('1245398'), ('32657891'), ('123658974'), ('7896534'), ('12354698'); DECLARE @FirstName TABLE (FirstNameID INT NOT NULL IDENTITY(1, 1), Name NVARCHAR(30) NOT NULL); INSERT INTO @FirstName (Name) VALUES ('Babak'), ('Carolin'), ('Martin'), ('Marie'), ('Susane'), ('Michail'), ('Ramona'), ('Ulf'), ('Dirk'), ('Sebastian'); DECLARE @LastName TABLE (LastNameID INT NOT NULL IDENTITY(1, 1), Name NVARCHAR(30) NOT NULL); INSERT INTO @LastName (Name) VALUES ('Bastan'), ('Krause'), ('Rosner'), ('Gartenmeister'), ('Rentsch'), ('Benn'), ('Kycik'), ('Leuoth'), ('Kamkar'), ('Kolaee'); DECLARE @Address TABLE (AddressID INT NOT NULL IDENTITY(1, 1), Addr NVARCHAR(100) NOT NULL); INSERT INTO @Address (Addr) VALUES ('Deutschlan Chemnitz Sonnenstraße 59'), (''), ('Deutschland Chemnitz Arthur-Strobel straße 124'), ('Deutschland Chemnitz Brückenstraße 3'), ('Iran Shiraz Chamran Blvd, Niayesh straße Nr.155'), (''), ('Deutschland Berlin Charlotenburg Pudbulesky Alleee 52'), ('United State of America Washington DC. Farbod Alle'), (''); DECLARE @RowsToInsert INT = 10000; ;WITH rowcounts AS ( SELECT (SELECT COUNT(*) FROM @TelNumber) AS [TelNumberRows], (SELECT COUNT(*) FROM @FirstName) AS [FirstNameRows], (SELECT COUNT(*) FROM @LastName) AS [LastNameRows], (SELECT COUNT(*) FROM @Address) AS [AddressRows] ), nums AS ( SELECT TOP (@RowsToInsert) (CRYPT_GEN_RANDOM(1) % rc.TelNumberRows) + 1 AS [RandomTelNumberID], (CRYPT_GEN_RANDOM(1) % rc.FirstNameRows) + 1 AS [RandomFirstNameID], (CRYPT_GEN_RANDOM(1) % rc.LastNameRows) + 1 AS [RandomLastNameID], (CRYPT_GEN_RANDOM(1) % rc.AddressRows) + 1 AS [RandomAddressID] FROM rowcounts rc CROSS JOIN msdb.sys.all_columns sac1 CROSS JOIN msdb.sys.all_columns sac2 ) -- INSERT dbo.Unsprstb(Firstname, Lastname, Tel, Address) SELECT fn.Name, ln.Name, tn.Num, ad.Addr FROM @FirstName fn FULL JOIN nums ON nums.RandomFirstNameID = fn.FirstNameID FULL JOIN @LastName ln ON ln.LastNameID = nums.RandomLastNameID FULL JOIN @TelNumber tn ON tn.TelNumberID = nums.RandomTelNumberID FULL JOIN @Address ad ON ad.AddressID = nums.RandomAddressID; 行数。
    • 由于此随机化的本质,可以使用重复行,而不是使用FULL JOIN将其过滤掉。但是,INNER JOIN不能与问题中的给定样本数据一起使用,因为每个数组/表变量中的元素数仅提供6300个唯一组合,并且要生成的请求行数为10,000。如果向表变量添加了更多值,使得可能的唯一组合总数超过请求的行数,则可以将@RowsToInsert关键字添加到DISTINCT CTE,或者查询可以是重新构建为DISTINCT所有表变量,包含DISTINCT字段,并使用nums抓取CROSS JOIN
    • ROW_COUNT()被注释掉,因此更容易看到上面的查询产生了所需的结果。只需取消注释TOP(n)即可让查询执行实际的DML操作。

答案 1 :(得分:1)

在SQL CLR C#代码中,你应该与显式服务器,数据库名称和凭证建立连接 - 而不是使用“context”connnectino:

NSStream