c# - 将整数作为 varbinary 发送到 SQL 触发器
问题描述
我使用此代码在 SQL 触发器中设置 context_info
string strUser = _app.Settings.User;
string strInt = "";
// strInt += '\x0100'; => becomes 1 in trigger
strInt += '\x0000';
strInt += '\x0200'; //=> becomes two in the trigger
strInt += '\u0000';
strInt += '\u0300';//=> becomes 3 in the trigger
// strInt += '\x0000';
// strInt += '\x0210'; //=> becomes 4098 in the trigger( 4096=1 + 2=2)
// strInt += '\x0000';
// strInt += '\u1300';//=> becomes 19 in the trigger
// strInt += '\x0200'; // = 131072
// strInt += '\x0000';
// strInt += '\u0100';// = 65536
// strInt += '\u0000';
//strInt += '\x0000';
//strInt += '\x0003'; //768
//strInt += '\u0000';
//strInt += '\u0002'; //512
string strUser = "myself";
string strContextValue = strInt + strUser + "$";
string strContext = "declare @context_info varbinary(120) set @context_info = CONVERT(varbinary(120), N'" + strContextValue + "') set context_info @context_info";
SqlCommand scContext = new SqlCommand(strContext, conn, tran);
scContext.ExecuteNonQuery();
在 Trigger 中,前 8 个字符被解释为两个数字:
declare @ctxt varbinary(128)
select @ctxt=context_info from master.dbo.sysprocesses where spid = @@spid
set @session=substring(@ctxt,1,4)
set @contextid=substring(@ctxt,5,4)
我不是触发器的作者,所以我无法更改它,但如果你能解释一下这里发生了什么样的魔法,那就太好了?如何确保前 8 个字符是数字?我如何对两个整数进行编码,以便它们在 SQL 触发器上变成 4 个字符长?
我已经做了很多编码的东西,但看不透。
当我将正确写入的(从另一个应用程序)数据加载回 ac#DataTable 时,我可以通过执行以下操作获得正确的数字值:
byte[] binaryString = (byte[])dataTable.Rows[0][2];
string x = Encoding.ASCII.GetString(binaryString);
string strSubX2 = x.Substring(3, 4);
int iResult = BitConverter.ToInt32(Encoding.Unicode.GetBytes(strSubX2), 0);
解决方案
看起来你的困惑来自于误解是什么varbinary
。varbinary
是一串字节,而不是字符。它甚至不是一个真正的字符串,它是一个字节数组。
varbinary
可以表示为十六进制值的字符串,而varbinary
T-SQL 代码中的常量则表示为十六进制字符串。每个字节两个十六进制字符。
T-SQL 中的SUBSTRING
函数可以处理字符串 ( varchar
) 和字节 ( varbinary
)。
句法
SUBSTRING(表达式,开始,长度)
start 和 length 的值必须以 、 或 数据类型的字符数和、、
ntext
或数据char
类型varchar
的字节数指定。text
image
binary
varbinary
所以,下面的 T-SQL 触发代码
declare @ctxt varbinary(128)
select @ctxt=context_info from master.dbo.sysprocesses where spid = @@spid
set @session=substring(@ctxt,1,4)
从字节数组中获取前 4 个字节(不是 chars)@ctxt
并将它们分配给@session
变量。什么类型@session
?很可能是int
。
在 SSMS 中运行此示例代码,您将看到一些整数的十六进制表示:
DECLARE @T TABLE (I int);
INSERT INTO @T VALUES
(0),
(1),
(3),
(19),
(255),
(256),
(512),
(768),
(4098),
(65535),
(65536),
(131072),
(1234567890),
(-1),
(-65535);
SELECT
I, CONVERT(varbinary(120), I) AS BinI
FROM @T;
结果
+------------+------------+
| I | BinI |
+------------+------------+
| 0 | 0x00000000 |
| 1 | 0x00000001 |
| 3 | 0x00000003 |
| 19 | 0x00000013 |
| 255 | 0x000000FF |
| 256 | 0x00000100 |
| 512 | 0x00000200 |
| 768 | 0x00000300 |
| 4098 | 0x00001002 |
| 65535 | 0x0000FFFF |
| 65536 | 0x00010000 |
| 131072 | 0x00020000 |
| 1234567890 | 0x499602D2 |
| -1 | 0xFFFFFFFF |
| -65535 | 0xFFFF0001 |
+------------+------------+
您在问题中的 C# 代码没有多大意义。将两者都包裹strInt + strUser
起来CONVERT(varbinary(120), N'" + strContextValue + "')
没有意义。
C# 代码应该构造二进制数组。T-SQL 触发器代码期望该数组的前 4 个字节是@session
,接下来的 4 个字节是@contextid
。字节,而不是字符。
所以,如果你想传递,比如说,131072
小数 as @session
,1234567890
小数 as @contextid
,加上myself$
字符串作为额外信息,你应该运行以下 T-SQL 代码:
declare @context_info varbinary(128); -- note 128 length here
set @context_info = 0x00020000;
-- note, there are no quotes around the constants, they are numbers, not strings
set @context_info = @context_info + 0x499602D2;
set @context_info = @context_info + CONVERT(varbinary(120), N'myself$');
-- note 120 length here, first 8 bytes are used by two integers
set context_info @context_info;
在 SSMS 中运行此代码,但将最后一行替换为SELECT @context_info;
以查看它的作用。您将得到以下结果:
+------------------------------------------------+
| (No column name) |
+------------------------------------------------+
| 0x00020000499602D26D007900730065006C0066002400 |
+------------------------------------------------+
您可以看到两个整数的二进制表示加上 unicode 文本字符串的二进制表示。
在 C# 中,您通过连接整数的十六进制表示来构造此代码。请注意,您不应将0x00020000
和括0x499602D2
在引号或CONVERT(varbinary, ...)
. 您应该只将myself$
字符串包含在CONVERT(varbinary, ...)
.
C# 代码应如下所示:
string strUser = "myself$";
int iSession = 131072;
int iContextID = 1234567890;
StringBuilder sb = new StringBuilder();
sb.Append("declare @context_info varbinary(120);");
sb.Append("set @context_info = 0x");
sb.Append(iSession.ToString("X8"));
sb.Append(";");
sb.Append("set @context_info = @context_info + 0x");
sb.Append(iContextID.ToString("X8"));
sb.Append(";");
sb.Append("set @context_info = @context_info + CONVERT(varbinary(120), N'");
sb.Append(strUser);
sb.Append("');");
sb.Append("set context_info @context_info;");
string strContext = sb.ToSTring();
SqlCommand scContext = new SqlCommand(strContext, conn, tran);
scContext.ExecuteNonQuery();
推荐阅读
- git - aix 7.1 上的 bitbucket ssh 克隆失败
- javascript - 如何使用 Javascript 在每个输入上重置 for 语句?
- django - Django 用户等于用户语句
- ios - iOS 图像显示在模拟器中,但不显示在设备上
- python - 如何为每个进程加载 psutil.cpu_percent 平均值?
- vuejs2 - VUE:通过 v-if 显示后选择输入
- r - 根据时间获取滚动平均值
- gitlab-ci-runner - Heroku 上的 Gitlab Runner
- elasticsearch - Elastic Search 按在另一个字段中定义的字段排序
- python - 如何从循环链表中删除每个备用元素