3

我目前正在使用 Master 数据库的 CONTEXT_INFO 属性来存储登录的用户名,以便稍后在表触发器中使用它进行审计。

在迁移到 SQL Azure 时,跨数据库连接的问题突然出现,我找不到该问题的直接解决方案。

以下是问题详情:

  1. 我从数据访问层调用存储过程 XXX 并将用户名作为参数传递
  2. 用户名用于设置 XXX 中的 CONTEXT_INFO 值
  3. 然后在表插入/更新/删除触发器中使用 CONTEXT_INFO 值来存储用户名以进行应用程序审计

到目前为止我找到的解决方案:

  1. 在数据库中创建表以用作 CONTEXT_INFO
  2. 在数据访问层中使用 2 个连接字符串,一个用于主数据库(设置 CONTEXT_INFO),另一个用于应用程序并在每次打开与我的应用程序的连接之前执行 SET CONTEXT_INFO

但我发现这两种解决方案都有风险,特别是将来在多个 SQL Azure 数据库上扩展数据库时。

感谢您的支持。

4

1 回答 1

4

我采取的方法如下所示。诀窍是检查是否在 SQL Azure 上运行,然后我们需要调用“SET CONTEXT_INFO ...”。这允许在本地 SQL Server Express 和 Azure 上执行相同的代码而无需更改。

  1. 创建一个表来存储上下文信息(不在 master 中,而是在同一个数据库中)

    CREATE TABLE [dbo].[ContextInfo] (
        [ContextInfo] varbinary(128) not null,
        [ApplicationUsername] nvarchar(128) not null,
        [UpdatedAt] datetime NOT NULL,
        CONSTRAINT [PK_UserContextInfo] PRIMARY KEY CLUSTERED ([ContextInfo] ASC)
    )
    
  2. 创建从应用程序调用的“设置上下文信息”的存储过程

    CREATE PROCEDURE [dbo].[SetContextInfo]
      @ApplicationUsername nvarchar(128)
    AS
    
    SET NOCOUNT ON
    
    -- Remove all context items older than an 5 minutes ago
    DELETE
      FROM [dbo].[ContextInfo]
     WHERE [UpdatedAt] < DATEADD(mi, -5, GETUTCDATE())
    
    --
    -- Use the MERGE command to do an update/insert
    -- See: http://technet.microsoft.com/en-us/library/bb510625.aspx
    --
    
    IF SERVERPROPERTY('edition') <> 'SQL Azure'
    BEGIN
        DECLARE @b varbinary(128)
        SET @b = CONVERT(varbinary(128),newid())
        EXEC sp_executesql @statement=N'SET CONTEXT_INFO @b',@params=N'@b varbinary(128)',@b=@b
    END
    
    DECLARE @ContextInfo varbinary(128)
    SELECT @ContextInfo = CONTEXT_INFO()
    
    MERGE [dbo].[ContextInfo] AS target
    USING (SELECT @ContextInfo, @ApplicationUsername) AS source ([ContextInfo], [ApplicationUsername])
       ON (target.[ContextInfo] = source.[ContextInfo])
     WHEN MATCHED THEN 
            UPDATE SET [ApplicationUsername] = source.[ApplicationUsername], [UpdatedAt] = GETUTCDATE()
     WHEN NOT MATCHED THEN  
            INSERT ([ContextInfo], [ApplicationUsername], [UpdatedAt])
            VALUES (source.[ContextInfo], source.[ApplicationUsername], GETUTCDATE());
    
  3. 创建一个存储过程来“获取上下文信息”

    CREATE PROCEDURE [dbo].[GetContextInfo]
    AS
        SET NOCOUNT ON
        DECLARE @ContextInfo varbinary(128)
        SELECT @ContextInfo = CONTEXT_INFO()
    
        SELECT [ApplicationUsername]
          FROM [dbo].[ContextInfo]
         WHERE [ContextInfo] = @ContextInfo
    GO
    
  4. 在触发源中,使用:

    DECLARE @UserContext TABLE ([Username] VARCHAR(128))
    INSERT INTO @UserContext (Username)
    EXEC [dbo].[GetContextInfo]
    

现在您将用户名存储在表变量中。如果您的应用程序之外的管理员应用了更改,您可能还需要检查用户名是否未设置并默认为 *SYSTEM_USER* 之类的名称。

于 2012-11-01T22:43:28.813 回答