EF Core中FromSqlRaw方法在XUnit测试中报错的问题
问题原因与解决方法
问题本质
EF Core的内存数据库提供者(InMemoryDatabase)不支持原生SQL查询,包括FromSqlRaw和FromSqlInterpolated方法。你在测试中用内存库替代真实数据库,但该提供者无法处理原生SQL对应的查询表达式,所以抛出了FromSqlQueryRootExpression未被处理的错误。
可行解决方案
改用SQLite内存模式做测试
SQLite支持原生SQL,且可配置为内存模式,既能快速运行测试,又能兼容FromSqlRaw调用。配置方式如下:// 测试中配置DbContext var connection = new SqliteConnection("Data Source=:memory:"); connection.Open(); // 必须保持连接打开,否则内存数据会丢失 var options = new DbContextOptionsBuilder<YourDbContext>() .UseSqlite(connection) .Options; using var context = new YourDbContext(options); context.Database.EnsureCreated(); // 初始化表结构用这个context执行测试,就能正常调用
FromSqlRaw了。抽象原生SQL逻辑,测试时替换实现
把调用FromSqlRaw的业务逻辑抽成独立接口,比如:public interface IYourRepository { Task<YourEntity> GetEntityAsync(int id); } // 生产环境实现 public class YourRepository : IYourRepository { private readonly YourDbContext _context; public YourRepository(YourDbContext context) => _context = context; public async Task<YourEntity> GetEntityAsync(int id) { return await _context.YourEntities .FromSqlRaw("SELECT * FROM YourEntities WHERE Id = {0}", id) .FirstOrDefaultAsync(); } }测试时,用模拟实现或内存数据返回结果,完全绕过原生SQL:
public class MockYourRepository : IYourRepository { public Task<YourEntity> GetEntityAsync(int id) { // 返回预设的测试数据 return Task.FromResult(new YourEntity { Id = id, Name = "Test" }); } }模拟DbSet跳过原生SQL执行
如果不想重构代码,也可以用Moq等框架模拟DbSet,让FromSqlRaw调用直接返回预设数据:var mockSet = new Mock<DbSet<YourEntity>>(); mockSet.Setup(m => m.FromSqlRaw(It.IsAny<string>(), It.IsAny<object[]>())) .Returns(mockSet.Object); mockSet.Setup(m => m.FirstOrDefaultAsync(It.IsAny<Expression<Func<YourEntity, bool>>>(), It.IsAny<CancellationToken>())) .ReturnsAsync(new YourEntity { Id = 1 }); var mockContext = new Mock<YourDbContext>(); mockContext.Setup(c => c.YourEntities).Returns(mockSet.Object); // 使用mockContext.Object执行测试
内容的提问来源于stack exchange,提问作者Katiso Koqo
相关产品推荐
相关产品推荐

