You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core使用SqlRaw添加实体后无法加载:值对象主键异常排查

问题原因分析

这个问题的核心在于EF Core对自定义值类型的转换逻辑,以及SQLite存储Guid的默认行为,和你手动原生SQL插入的方式不匹配,具体拆解如下:

1. SQLite中Guid的存储格式不匹配

EF Core默认会将Guid类型(通过你的DeviceId值转换映射的Value属性)存储为SQLite的BLOB(二进制)类型。但你在AddDevice方法中,直接将Guid转为字符串拼接进SQL语句:

string sqlInsert = $"INSERT INTO \"Devices\" (\"Id\") VALUES ('{id}')";

这会导致数据库中存储的是字符串形式的Guid,而EF Core执行查询时,会把x.Id == new DeviceId(id)转换为对二进制Guid值的比较——字符串和二进制的Guid自然无法匹配,所以First()会返回null。

而你提到的两种正常情况也能佐证这一点:

  • 当主键改为int时,字符串和整数的转换逻辑简单,EF Core能正确处理,所以查询正常;
  • 通过DbContext添加实体时,EF Core会自动按照配置的转换规则(将DeviceId.Value的Guid转为二进制)插入数据,存储格式和查询时的预期一致,所以能正常找到。

2. EF Core变更跟踪器未感知原生SQL插入的实体

另外,使用ExecuteSqlRaw执行原生SQL插入后,EF Core的变更跟踪器并不会自动同步这条新记录。虽然Assert.NotEmpty(context.Devices)能通过(因为它会查询数据库),但如果你的查询逻辑优先检查本地跟踪的实体(某些场景下),也可能出现不一致——不过这不是你当前断言失败的主要原因,核心还是存储格式问题。

解决方法

针对这个问题,有两种可靠的修复方式:

方式一:使用参数化查询让EF Core处理Guid转换

不要手动拼接Guid字符串,而是用参数化查询让EF Core自动处理类型转换,确保存储格式和预期一致:

private void AddDevice(MyDbContext context, Guid id)
{
    string sqlInsert = "INSERT INTO \"Devices\" (\"Id\") VALUES (@id)";
    context.Database.ExecuteSqlRaw(sqlInsert, new SqlParameter("@id", id));
}

参数化查询不仅能解决格式匹配问题,还能避免SQL注入风险,是更安全的做法。

方式二:配置SQLite将Guid存储为TEXT类型

如果你需要保持原生SQL插入字符串格式的Guid,可以在DbContext配置中指定SQLite的Guid存储格式为TEXT:

services.AddDbContext<MyDbContext>(options => 
    options.UseSqlite(connection, b => b.UseSqlGuidFormat(SqliteGuidFormat.Text)));

这样EF Core会将Guid以字符串形式存储和查询,和你手动插入的格式匹配,就能正常找到实体了。

内容的提问来源于stack exchange,提问作者Mojtaba Khooryani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 15:18:00