目前我正在使用SQL Server 2016来利用Always Encrypted
功能。我应该加密几列。我已经用SQL Server加密了这些列。NHibernate
可以轻松地从SQL Server
读取数据,但是当它尝试在数据库中插入数据时,它将抛出如下所示的exe:
操作数类型冲突:nvarchar(4000) 使用 (encryption_type = 加密 "确定性",encryption_algorithm_name = "AEAD_AES_256_CBC_HMAC_SHA_256", column_encryption_key_name = "CEK_Auto1",column_encryption_key_database_name = "EncTest")是 与 nvarchar(250) 不兼容,使用 (encryption_type = 加密 "确定性",encryption_algorithm_name = "AEAD_AES_256_CBC_HMAC_SHA_256", column_encryption_key_name = "CEK_Auto1", column_encryption_key_database_name = "EncTest")
这是我对NHibernate
中特定列的列映射:
<column name="DisableTxt" length="100" sql-type="NVarChar" />
我应该在hbm
文件中定义什么映射?
我找到了这个问题的解决方案,首先我想描述一下为什么NHibernate
不能在Always Encrypted
功能中使用Encrypted Columns
:
当我们在连接字符串中启用AlwaysEncrypted
时,ADO.NET
sp_describe_parameter_encryption
任何数据库操作之前自动执行存储过程,以确定哪些参数对应于使用"始终加密"功能保护的数据库列。此 sp 对字段的长度很敏感,如果指定的参数长度不等于列长度,SQL Server 将给出以下错误:
操作数类型冲突:nvarchar(4000) 使用 (encryption_type = 加密 "确定性",encryption_algorithm_name = "AEAD_AES_256_CBC_HMAC_SHA_256", column_encryption_key_name = "CEK_Auto1",column_encryption_key_database_name = "EncTest")是 与 nvarchar(250) 不兼容,使用 (encryption_type = 加密 "确定性",encryption_algorithm_name = "AEAD_AES_256_CBC_HMAC_SHA_256", column_encryption_key_name = "CEK_Auto1", column_encryption_key_database_name = "EncTest")
发生这种情况是因为NHibernate
始终为NVarchar
列定义4000
的参数大小(如果列长度不是NVarchar(max)
)。所以想象一下,我们有一个长度为30
的列,但NHibernate
为指定列定义一个长度为4000
的参数。NHibernate
为什么要这样做?
如果您查看SqlClientDriver.cs
源代码上的第 146 行Nhibernate
您将看到以下评论:
不要使用来自 SqlType,因为 LIKE 表达式需要/
/更大的列。 https://nhibernate.jira.com/browse/NH-3036
那么我们如何解决这个问题呢?我们可以为定义准确参数长度的NHibernate
创建一个新Driver
。(当然,如果你不在乎%%
喜欢的表情)。(我已经在NHiberate 3.x
中使用了此方法)
public class NewDriver : NHibernate.Driver.Sql2008ClientDriver
{
public override IDbCommand GenerateCommand(CommandType type, SqlString sqlString, SqlType[] parameterTypes)
{
IDbCommand command = base.GenerateCommand(type, sqlString, parameterTypes);
NewDirver.SetParameterSizes(command.Parameters, parameterTypes);
return command;
}
public static void SetParamterSizes(IDataParameterCollection parameters, SqlType[] parameterTypes)
{
for(int index=0;index<parameters.Count;++index)
{
NewDriver.SetVariableLengthParameterSize((IDbDataParameter)parameters[index], parameterTypes[index]);
}
}
public static void SetVariableLengthParmaeterSize(IDbDataParameter dbParam, SqlType sqlType)
{
SqlClientDriver.SetDefaultParameterSize(dbParam, sqlType);
if(sqlType.LengthDefined && !IsText(dbParam, sqlType) && !IsBlob(dbParam, sqlType))
{
dbParam.Size = sqlType.Length;
}
if(sqlType.PrecesionsDefined)
{
dbParam.Precision = sqlType.Precision;
dbParam.Scale = sqlType.Scale;
}
}
}