SQL Server 2016 Always Encrypt加密列后,存储过程插入失败及刷新咨询
解决Always Encrypted列通过存储过程插入失败的问题
我之前处理过好几个类似的场景,核心问题是存储过程创建时没跟上加密列的配置,加上EF可能缓存了旧元数据导致的。下面是一步步的解决方法:
1. 重新创建存储过程,匹配加密列的参数类型
当你给列开启Always Encrypted后,存储过程里对应加密列的参数必须和加密列的精确类型完全一致(包括长度、是否可为空等),而且SQL Server需要识别到这个参数是和加密列关联的。
举个实际例子,如果你的加密列是NVARCHAR(50) ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [YourCEK], ENCRYPTION_TYPE = DETERMINISTIC, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'),那存储过程得这么写:
CREATE OR ALTER PROCEDURE dbo.InsertEncryptedRecord @EncryptedCol NVARCHAR(50) -- 必须和加密列的类型、长度完全匹配 AS BEGIN SET NOCOUNT ON; INSERT INTO YourTargetTable (EncryptedCol) VALUES (@EncryptedCol); END
注意:如果这个存储过程是在加密列创建前就存在的,必须用CREATE OR ALTER重新生成,不能只修改内容——这样SQL Server才会重新关联参数和加密列的加密属性。
2. 确保EF连接启用列加密
ASP.NET MVC这边,你得保证EF的连接字符串里包含Column Encryption Setting=Enabled,不然应用端没法正确加密参数再传给存储过程。
要么直接在连接字符串里加:
Server=YourSQLServer;Database=YourDB;User Id=YourUser;Password=YourPass;Column Encryption Setting=Enabled;
要么在DbContext的构造函数里手动配置:
public YourDbContext() : base("YourConnectionString") { var sqlConn = (SqlConnection)((IObjectContextAdapter)this).ObjectContext.Connection; sqlConn.ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Enabled; }
3. 刷新EF的存储过程元数据
很多时候问题出在EF缓存了旧的存储过程元数据,即使你改了SQL端的存储过程,EF还是用旧的参数映射。
- 如果是Database First:打开你的EDMX文件,右键空白处选「Update Model from Database」,在弹出的窗口里找到你的存储过程,重新导入更新模型。
- 如果是Code First:确保你用
FromSqlRaw或者ExecuteSqlCommand调用存储过程时,参数的类型和加密列一致;如果用了存储过程映射,重新生成对应的实体类或函数导入。
4. 验证存储过程的参数配置
你可以跑下面的SQL查询,确认存储过程的参数已经和加密列关联上了:
SELECT p.name AS 参数名, t.name AS 数据类型, c.is_encrypted AS 是否加密, c.column_encryption_key_id AS 加密密钥ID FROM sys.parameters p JOIN sys.types t ON p.system_type_id = t.system_type_id LEFT JOIN sys.columns c ON p.name = c.name AND p.object_id = c.object_id WHERE p.object_id = OBJECT_ID('dbo.InsertEncryptedRecord');
如果是否加密列显示1,说明参数已经正确关联了加密设置。
避坑提醒
- 别在存储过程里对加密列参数做字符串操作(比如SUBSTRING、拼接),因为加密列在SQL Server端是无法解密的,这类操作直接会报错。
- 确保你的应用程序服务账号有访问列加密密钥(CEK)的权限,不然即使存储过程配置对了,也会因为密钥访问失败报错。
内容的提问来源于stack exchange,提问作者Melody
相关产品推荐
相关产品推荐

