.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

可以在生产中使用 Kubernetes 自动缩放 v2beta2 作为 apiVersion 吗?

如何修复在生产中失败的应用内购买?

Spring boot中最大连接数最大线程数与最大等待数在生产中的异常场景