.Where 子句在测试时自动排序列表,但在生产中不排序
Posted
技术标签:
【中文标题】.Where 子句在测试时自动排序列表,但在生产中不排序【英文标题】:.Where clause auto orders a list when testing, but not in production 【发布时间】:2022-01-08 22:13:37 【问题描述】:我有一个 API 端点,它调用以下函数来获取设备的历史通过失败状态:
这是从控制器接收设备ID的函数:
var Id = GetId(equipmentId);
return new HistoricTestStatusDto
EquipmentId = equipmentId,
HistoricTestStatusDate = _dbContext.HistoricResultsQueryable
.Where(x => x.Id == Id)
.Select(f => new HistoricTestStatusDateDto
DateOfOperation = f.EventDate.Date,
FailCount = f.NumberOfFails,
PassCount = f.NumberOfPasses
)
;
当通过 API 调用返回时,它会返回,但是数据库 (MS-SQL) 会返回它,这通常不是 DateOfOperation 顺序。
HistoricResults
是一个不按返回结果排序的视图,因此它们可以按任何顺序返回。
[Table("HistoricResults")]
public class HistoricResults
public string Id get; set;
public int NumberOfFails get; set;
public int NumberOfPasses get; set;
public DateTime EventDate get; set;
我将其选择到一个新的 DTO HistoricTestStatusDateDto 中:
public class HistoricTestStatusDateDto
public DateTime DateOfOperation get; set;
public int PassCount get; set;
public int FailCount get; set;
EF 模型是这样设置的:
modelBuilder.Entity<HistoricResults>()
.HasKey(x => new x.Id, x.EventDate );
测试
下面的测试应该失败,因为它不应该被订购,我知道我需要在.Select
之后添加一个.Order
,但是由于这篇文章中提到的问题它通过了。
NUnit 测试设置使用内存数据库中的 Sqlite:
private void CreateDatabase()
_db = new SqliteConnection("DataSource=:memory:");
_db.Open();
_dbContext = new Context(CreateDb<Context>(_db));
_dbContext.Database.EnsureCreated();
测试看起来像这样,其中 dateOfOperations 没有按特定顺序:
var dateOfOperation = new DateTime(2021, 11, 30);
_dbContext.HistoricResults.AddRange(new List<HistoricResults>
CreateHistoricResult("Id1", EquipmentId1, dateOfOperation.AddDays(5)),
CreateHistoricResult("Id1", EquipmentId1, dateOfOperation.AddDays(3)),
CreateHistoricResult("Id1", EquipmentId1, dateOfOperation.AddDays(2)),
CreateHistoricResult("Id1", EquipmentId1, dateOfOperation.AddDays(6)),
CreateHistoricResult("Id1", EquipmentId1, dateOfOperation.AddDays(1)),
CreateHistoricResult("Id1", EquipmentId1, dateOfOperation.AddDays(4)),
CreateHistoricResult("Id1", EquipmentId1, dateOfOperation.AddDays(7))
);
_dbContext.SaveChanges();
var historicTestStatusDto = _sut.HistoricTestStatusCalc(EquipmentId1);
historicTestStatusDto.Should().NotBeNull();
historicTestStatusDto.HistoricTestStatusDate.Should().HaveCount(7);
historicTestStatusDto.HistoricTestStatusDate.Select(x => x.DateOfOperation).Should().BeInAscendingOrder();
在调试测试和检查historicTestStatusDto.HistoricTestStatusDate
时,尽管我没有进行任何排序,但它似乎是有序的,但是当在生产中从 API 调用时,它会以随机顺序返回(从数据库发回查看)。
在对此进行更多挖掘之后,似乎.Where
在使用 SQLite 进行测试时实际上是在对数据进行排序
_dbContext.HistoricResultsQueryable
.Where(x => x.Id == Id)
TL;DR
在测试时使用 SQLite 数据库,.Where
子句似乎按DateOfOperation
升序排序,但是当使用 MS-SQL 在生产中运行时,它返回数据,但它是从数据库视图返回的。
【问题讨论】:
当你没有明确地提供一个顺序时,结果的顺序是没有定义的并且总是可以变化的——你应该不依赖它;是的,它可以是您偶然想要的顺序。在您的情况下,它取决于底层数据库提供程序。如果订单很重要,您必须添加您自己的OrderBy()
。
如果您需要订购的数据,请告诉数据库订购它,否则没有保证您将获得您需要的订购数据。类似地,如果你在代码中使用集合,你应该使用Sorted Collection Types 来确保顺序是你想要的。
【参考方案1】:
您看到的行为可能是特定于 DB Provider 实现的。数据库系统使用索引和其他方式构建查询,一些提供者最终可能会根据索引使用或其他因素得出结果排序。
指定 order by 子句表明查询应按特定顺序提供结果。
未指定顺序表示查询的消费者不关心结果的顺序,所以这里的错误是您的测试关心的是订购...
不指定 order by 并不意味着“按添加的顺序返回”,或“按随机顺序返回”,或者当然不是“确保这些不按顺序返回”。
【讨论】:
以上是关于.Where 子句在测试时自动排序列表,但在生产中不排序的主要内容,如果未能解决你的问题,请参考以下文章
无法连接到远程服务器(ftp 在开发中有效,但在生产中无效)
在生产中捕获 UIViewAlertForUnsatisfiableConstraints