我想创建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,TaskCompletionSource1 retry, DbConnectionOptions userOptions)
1重试)
bei System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource
bei System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1重试)
bei System.Data.SqlClient.SqlConnection.Open()
bei StoredProcedures.InsertingRows()
我该如何解决这个问题?
答案 0 :(得分:4)
此代码中存在一些需要解决的问题:
关于所述问题,当您收到 System.Security.SecurityException 错误时,该错误指的是尝试到达数据库外部的代码,这是{ {1}}装配。你如何解决这个问题取决于你想要完成的任务。
SAFE
。要将程序集设置为PERMISSION_SET
以外的任何其他值,您需要:
EXTERNAL_ACCESS
该登录的权限。与其他方法相比,此方法非常首选:SAFE
。如果无法对程序集进行签名,则此方法只能用作最后的手段。或者用于快速测试目的。将数据库设置为EXTERNAL ACCESS ASSEMBLY
会使您的实例面临潜在的安全威胁,应该避免,即使比其他方法更快/更容易。如果您尝试访问已登录的SQL Server实例,则可以选择使用TRUSTWORTHY ON
的进程内连接,这可以在{{1}中完成}} 部件。这就是@Marc在他的回答中建议的。虽然使用这种类型的连接肯定有好处,虽然在这种特定情况下Context Connection是合适的选择,但是它过于简单和不正确地声明你总是使用这种类型的连接。让我们来看看上下文连接的正面和负面方面:
TRUSTWORTHY ON
程序集中完成。Context Connection = true;
。否定:
所有这些&#34;否定&#34;在使用常规/外部连接时允许使用,即使它是从您执行此代码的同一实例。
如果要连接到正在执行此代码的实例并使用外部/常规连接,则无需指定服务器名称甚至使用SAFE
。首选语法是SAFE
,它使用共享内存,而其他语法有时可能使用效率不高的TCP / IP。
除非您有非常具体的理由,否则请勿使用CONTEXT_INFO
localhost
Server = (local)
是一种很好的做法
在Persist Security Info=True;
循环之前调用Dispose()
更有效,然后在循环内部,只需通过SqlCommand
设置值,您已经在这样做,只需将insertcommand.Parameters.Add()
行移到for
行之前。
firstname.Value =
/ insertcommand.Parameters.Add()
/ for
为tel
而不是@tel
/ listtelnumber
。电话号码,就像邮政编码和社会安全号码(SSN)一样,不是号码,即使它们看起来像是。 INT
无法存储前导VARCHAR
或类似string
的内容来表示&#34;扩展名&#34;。
所有这一切,即使以上所有内容都得到了纠正,这个代码仍然存在一个应该解决的问题:这是一个相当简单的操作在直接的T-SQL中,在SQLCLR中执行此操作过于复杂,维护起来更难,成本更高,而且速度更慢。此代码执行10,000个单独的事务,而它可以很容易地作为单个基于集的查询(即一个事务)完成。您可以将INT
循环包装在一个可以加快它的事务中,但它仍然总是比基于集合的T-SQL方法慢,因为它仍然需要发出10,000个单独的0
语句。您可以使用SQL Server 2008中引入的ex.
或CRYPT_GEN_RANDOM轻松地在T-SQL中随机化。(请参阅下面的更新部分)
如果您想了解有关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