【问题标题】:Connect to another database in SQL Server instance from within 'Safe' CLR database trigger从“安全”CLR 数据库触发器中连接到 SQL Server 实例中的另一个数据库
【发布时间】:2012-09-13 06:22:31
【问题描述】:

我正在为 SQL Server 2008 R2 编写 CLR 更新触发器。触发器需要将更新的值写入托管在同一 SQL Server 实例中的另一个数据库中的表中。当我尝试从触发器中打开使用以下连接字符串创建的连接时,我收到“SecurityException”:

...new SqlConnection("Data Source=(local);Initial Catalog=[my database];Integrated Security=True")

非常希望我将程序集的权限级别保留为SAFE。我很确定我必须将程序集的权限级别设置为EXTERNAL_ACCESS 才能连接到远程数据库,但是是否可以使用SAFE 权限级别连接到同一 SQL Server 实例中的另一个数据库?

谢谢。

【问题讨论】:

  • 将跨数据库(或跨服务器)活动推迟到触发器触发之后几乎总是更好。您通常不希望第二个数据库(或任何其他外部资源)不可用来阻止原始事务完成。

标签: c# sql-server triggers sql-server-2008-r2 sqlclr


【解决方案1】:

是的,绝对可以在 SQLCLR 触发器中引用另一个数据库,该触发器位于标记为 SAFE 的程序集内。问题中遇到的错误仅仅是由于使用了常规/外部连接字符串,这需要PERMISSION_SETEXTERNAL_ACCESS。但是使用"Context Connection = true;" 的进程内/内部连接字符串允许您运行任何您想要的查询,包括通过三部分对象名称引用另一个数据库的查询。

我可以使用以下代码做到这一点:

主表(在 TestDB1 中):

CREATE TABLE dbo.Stuff
(
    [Id] INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, 
    [Something] NVARCHAR(50) NOT NULL
);

审计表(在 TestDB2 中):

CREATE TABLE dbo.AuditLog
(
    AuditLogID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, 
    EventTime DATETIME NOT NULL DEFAULT (GETDATE()),
    BeforeValue NVARCHAR(50) NULL,
    AfterValue NVARCHAR(50) NULL
);

主表上的 SQLCLR 触发器(部分代码):

string _AuditSQL = @"
    INSERT INTO TestDB2.dbo.AuditLog (BeforeValue, AfterValue)
        SELECT del.Something, ins.Something
        FROM   INSERTED ins
        FULL OUTER JOIN DELETED del
                     ON del.Id = ins.Id;
";

SqlConnection _Connection = new SqlConnection("Context Connection = true");
SqlCommand _Command = _Connection.CreateCommand();
_Command.CommandText = _AuditSQL;

try
{
    _Connection.Open();
    _Command.ExecuteNonQuery();
}
finally
{
    _Command.Dispose();
    _Connection.Dispose();
}

测试查询:

USE [TestDB1];
SELECT * FROM dbo.Stuff;
---
INSERT INTO dbo.Stuff (Something) VALUES ('qwerty');
INSERT INTO dbo.Stuff (Something) VALUES ('asdf');

SELECT * FROM dbo.Stuff;
SELECT * FROM TestDB2.dbo.AuditLog;
---
UPDATE  tab
SET     tab.Something = 'dfgdfgdfgdfgdfgdfgd'
FROM    dbo.Stuff tab
WHERE   tab.Id = 2;

SELECT * FROM dbo.Stuff;
SELECT * FROM TestDB2.dbo.AuditLog;

【讨论】:

  • @bdebaere 我也刚刚注意到您问了一个未标记为排序规则的排序规则/编码问题......我会回答那个......
  • @bdebaere 听起来不错。接受(我相信你已经看到了)。另外,如果这个答案对你有帮助,请不要忘记投票:-)
【解决方案2】:

这似乎是不可能的。但是,T-SQL 语句可以使用 [DatabaseName].[dbo].[TableName] 引用同一实例中的另一个数据库。我可以在我的 CLR 触发器中处理杂乱的逻辑,然后通过调用一个简单的 T-SQL 存储过程并传入参数来最终插入到第二个数据库中。

【讨论】:

  • 安德鲁,这是可能的。详情请看我的回答。
猜你喜欢
  • 2014-09-17
  • 1970-01-01
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多