在C#中,我有一个名为UniqueId的byte[]
字段。我将此字段存储为SQL Server数据库(EF6)中的二进制(16)。
我注意到有时候,存储的guid是15个字节而不是16个字节。当我从db中检索值后执行var guid = new Guid(uniqueId)
时,这会导致以下异常:
System.ArgumentException:GUID的字节数组必须长度为16个字节。
在调查之后,我注意到每当我生成的guid在十六进制末尾包含“00”时,SQL会截断它!
例:
生成的Guid(通过Guid.NewGuid):FF96F954777E8941A04774CD157C5C00(16字节)
SQL Server中存储的二进制文件(16):0xFF96F954777E8941A04774CD157C5C(15字节)
如果你注意到,最后的00
会被截断。因此,如果我查询此字段并尝试将其字节转换为Guid,我将获得System.ArgumentException
。
还有其他人遇到这个问题吗?解决方法是什么?我正在考虑Guid
的包装器,它一直生成guid,直到它没有尾随零,但这看起来很hacky。
更新1:既然你们要求它,我运行了SQL分析器,这是由EF生成的SQL(删节版本):
exec sp_executesql N'INSERT [dbo].[customers]([UniqueId])
VALUES (@0)',N'@0 varbinary(max)',@0=0xFF96F954777E8941A04774CD157C5C00
如果列的BINARY(16)
设置为ANSI_PADDING
且列允许OFF
(NULL
),则会在demo中看到此行为。
修复它理想情况下将涉及更改表定义,以便列使用uniqueidentifier
数据类型或至少具有ANSI_PADDING
。
要更改ANSI_PADDING
语义将涉及SET ANSI_PADDING ON
然后添加一个新的binary(16)
列 - 从旧列填充它,然后删除并重命名。这就是如果您的应用程序可以容忍可能重新排序列,如果它无法处理此重新排序并且UniqueId
不是表中的最后一列,则需要创建新表并迁移所有数据。
如果您不想更改数据库设置,您可以随时填充从数据库返回的内容,例如
byte[] uniqueId = new byte[15]; //however this comes back from the db;
byte[] uniqueIdPadded = new byte[16];
uniqueId.CopyTo(uniqueIdPadded, 0);
var guid = new Guid(uniqueIdPadded);