实现一种“伪”委托而不需要在应用程序/数据库代码中进行大量更改,我建议使用
context_info()来传输当前用户到数据库,并将对
user_name()
的调用替换为对
dbo.fn_user_name()
的调用。
以下是如何构建此解决方案的示例:
创建fn_user_name()函数
我会创建一个名为fn_user_name的函数,它将从连接的context_info()中提取用户名,或者在没有可用的context info时返回user_name()。请注意,连接上下文是一个128字节的二进制。您放置在其中的任何内容都将用零字符填充,为了解决这个问题,我使用空格填充值。
create function dbo.fn_user_name()
returns sysname
as
begin
declare @user sysname = rtrim(convert(nvarchar(64), context_info()))
if @user is null
return user_name()
return @user
end
go
现在您需要在代码中查找并替换所有对 user_name() 的调用,并将它们替换为此函数。
在 .net 中嵌入上下文到数据库调用
这里有两个选项。或者您创建自己的 SqlConnection 类,或者创建一个工厂方法,该方法将返回一个打开的 SqlConnection,如下所示。工厂方法的问题是您运行的每个查询都将是 2 个数据库调用。不过这是最少要编写的代码。
public SqlConnection CreateConnection(string connectionString, string user)
{
var conn = new SqlConnection(connectionString);
using (var cmd = new SqlCommand(
@"declare @a varbinary(128) = convert(varbinary(128), @user + replicate(N' ', 64 - len(@user)))
set context_info @a", conn))
{
cmd.Parameters.Add("@user", SqlDbType.NVarChar, 64).Value = user;
conn.Open();
cmd.ExecuteNonQuery();
}
return conn;
}
你会将这个用作:
using(var conn = CreateConnection(connectionString, user))
{
var cmd = new SqlCommand("select 1", conn);
return conn.ExecuteScalar()
}
对于 SqlConnection 的替代版本,您需要重载 DbConnection 并实现 SqlConnection 的所有方法。执行方法将在查询之前添加以下查询,并将用户名作为额外参数传递。
declare @a varbinary(128) = convert(varbinary(128), @user + replicate(N' ', 64 - len(@user)))
set context_info @a
那个类将被用作:
using(var conn = new SqlContextInfoConnection(connectionString, user))
{
var cmd = new SqlCommand("select 1", conn);
conn.open;
return conn.ExecuteScalar()
}
我个人会实现选项2,因为它更接近普通SqlConnection的工作方式。
App_Name()
或Host_Name()
函数结合使用?虽然不是理想的解决方案,但考虑到限制,这可能是一个可接受的解决方法... - gvee