【问题标题】:Always Encrypted SQL 2016 and Entity Framework 6: “Operand type clash: datetime2 is incompatible with date”Always Encrypted SQL 2016 和实体框架 6:“操作数类型冲突:datetime2 与日期不兼容”
【发布时间】:2017-03-15 08:57:59
【问题描述】:

当前项目:

  • MVC 5.2
  • 点网 4.6.2
  • EF 6
  • 身份 2(根据以下存储库模式修改)
  • SQL Server 2016 RTM
  • 存储库模式:Persistence-Ignorant ASP.NET Identity
  • 始终加密列加密,如 herehere 所述
  • EntityTypeConfiguration 中的所有 SQL 为 MapToStoredProcedures();

与存储库模式教程不同,我能够将模型推送到数据库。我不是在使用手工组装的数据库。我确实根据第二个 Always Encrypted 链接中的两个 SQL 语句修改了迁移,对于出生日期和社会安全号码也不足为奇。

因为我的实体框架实体指定了加密字段,

Property(x => x.Dob).HasColumnOrder(15).HasColumnName("Dob").HasColumnType("Date").IsRequired();
Property(x => x.Sin).HasColumnOrder(16).HasColumnName("Sin").HasColumnType("nvarchar").HasMaxLength(11).IsRequired();

我在我的User 域实体中用以下方式修饰了这两个字段:

[DataType(DataType.Date)]
public DateTime Dob { get; set; }
[MaxLength(11)]
public string Sin { get; set; }

根据this comment作为预防措施,我还在我的IdentityUser.cs 文件中修饰了相同的字段。

当我尝试推送我尝试创建的默认管理用户时,使用:

public async Task<ActionResult> AddAdmin() {
  var user = _userManager.FindByName("email@domain.net");
  if(user == null) {
    user = new IdentityUser() {
      // Other fields
      Dob = new DateTime(1972, 10, 12),
      Sin = "726-261-050",
      // Other fields
    };
    var createUser = await _userManager.CreateAsync(user, "password");
    if (createUser.Succeeded) {
      var userId = _userManager.FindByName("email@domain.net").Id;
      var addRole = await _userManager.AddToRoleAsync(userId, "Admin");
      if (addRole.Succeeded) {
        return View("Index");
      }
    }
  }
}

我收到以下错误:

操作数类型冲突:datetime2(7) 用 (encryption_type = “随机”,加密算法名称 = 'AEAD_AES_256_CBC_HMAC_SHA_256',column_encryption_key_name = 'CEK1', column_encryption_key_database_name = 'database') 不兼容 用 (encryption_type = 'RANDOMIZED', 加密算法名称 = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'CEK1', column_encryption_key_database_name = '数据库')

发生在_userManager.CreateAsync()

我完全不知道为什么会这样。我在IdentityUser() 中明确指定DobDate 字段,而不是DateTime2。原始 User 模型/域及其关联的 EF 实体也是如此。实际的 DB 字段是 Date 字段(已亲自确认),所以我可以看到为什么如果源被神秘地创建为 DateTime2 时目标是一个问题,即使 IdentityUser() Dob 字段是Date 也是。理论上,IdentityUser() 中的Date 字段不能将DateTime2 传递给数据库。

【问题讨论】:

    标签: sql-server asp.net-mvc sql-server-2016 asp.net-mvc-5.2 always-encrypted


    【解决方案1】:

    我相信 EF 将所有日期类型作为 datetime2 处理。将 SQL Server 中的列类型更改为 datetime2 应该会有所帮助。到目前为止,您唯一的选择是在 SQL Server 中将列的数据类型更改为 datetime2,因为 EF 没有任何挂钩到参数生成。

    【讨论】:

    • 第一篇 Always Encrypted 文章的作者不知何故设法使其与 Date 一起使用,他们只是不清楚如何。此外,出生日期只是日期,没有时间;这使它更有效,因为它使用更少的空间。如果 EF 仅适用于 datetime2,那么通常如何填写数据库中的日期字段?为什么当 EF 传递相同类型的(未加密的)数据时,未加密的 Date 字段不会引发相同的“不兼容”错误?
    • 我想我可以看到你来自哪里——一个未加密的日期插入到一个日期字段中只是截断,而如果它作为一个已经加密的datetime2到达,那会扔一个大扳手作品。那么在加密之前如何进行转换呢?
    • 你说得对,对于明文数据,SQL server 会做 datetime2 到 datetime 的转换。我将创建一个与旧表具有相同架构的新表(表 2),但是将日期更改为 datetime2。请记住,表 2 仍然是纯文本。然后使用 SQL 批量复制将数据从旧表复制到表 2。然后使用 ssms 中的始终加密向导加密相应的列。
    • 那么如何通过实体框架或 MVC 编写脚本呢?请记住,我们谈论的是活跃用户注册,员工创建包含生日和 SSN 的员工门户帐户以用于帐户验证。
    • 其实你可以尝试使用alter table alter column来改变现有列的数据类型,见msdn.microsoft.com/en-us/library/ms190273.aspx。当然,在生产环境中进行更改之前,您有责任在测试环境中测试工作流程。
    【解决方案2】:

    如果我们不想将 datetime 的数据类型更改为 datetime2。因为 datetime 延长了最后一个小数点后的 7 位。

    通过以下脚本检查。黑白日期时间和日期时间2的区别。

    select * from newdatetimetest ALTER TABLE newdatetimetest ALTER column mydatetime datetime2

    select * from newdatetimetest ALTER TABLE newdatetimetest ALTER column mydatetime datetime

    select * from newdatetimetest

    您可以实现自定义拦截器将实体框架 datetime2 数据类型转换为日期时间。

    我们已经成功实现并成功插入记录。

    在链接下方找到参考表格。

    https://github.com/aspnet/EntityFramework6/issues/578

    https://github.com/aspnet/EntityFramework6/pull/1147

    【讨论】:

    • 实施成功。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-03
    • 1970-01-01
    • 2019-10-31
    • 2016-07-09
    • 1970-01-01
    • 1970-01-01
    • 2020-04-27
    相关资源
    最近更新 更多