在Visual Studio中未引发预期的异常,Linq Select语句意外地过滤了结果

问题描述

我遇到了一个非常奇怪的情况,我曾希望使用C#和Linq到Entity的一段代码抛出一个异常,但是在Visual Studio中却没有,但是在Linqpad中却发生了,我想知道是否有人能够解释为什么。以下是经过测试的代码段和情况。

这是令人讨厌的代码块,提取修改模板的用户的信息:

Db.CommunicationTemplates.Select(i => new CommunicationTemplateModel
{
     ModificationUserId = (int)i.ModificationUserId,ModificationFirstName = i.ModificationUser.FirstName,ModificationLastName = i.ModificationUser.LastName
})

我的一位同事提交了此更改以进行测试。我的开发人员在不应将数据库错误地设置为可为空的情况下

public int? ModificationUserId { get; set; }

这种情况下的测试数据非常简单,我们有3条记录。 2具有空的ModificationUserId,其中一个具有值,例如

为空 123456 空

这是具有名字和姓氏的用户表的外键。

我曾期望该代码块引发强制转换异常,但它只是滤除了违规行为,仅成功返回了包含“ 123456”的一行。为了测试代码,我将查询弹出到Linqpad中并运行它,这样做的确得到了预期的错误

The null value cannot be assigned to a member with type system.int32 which is a non-nullable value type

这就是为什么,为什么要在Linqpad中而不是在Visual Studio中抛出该错误

在我们的Visual Studio代码中,我将该行简化为:

Db.CommunicationTemplates.Select(i => (int)i.ModificationUserId)

令我惊讶的是,它确实在Linqpad和Visual Studio中都引发了类型转换错误错误。之后,我回过头来尝试了以下代码

CommunicationTemplates.Select(i => new 
{
    ModificationFirstName = i.ModificationUser.FirstName,ModificationLastName = i.ModificationUser.LastName
})

再次令我惊讶的是,它返回的结果不一致。在Visual Studio中,这仅成功返回了“ 123456”记录,而在LinqPad中,它仅返回了所有3条记录。我总是希望Select语句在过滤结果之前会引发错误,因为select语句确实不应该过滤内容

所以我的问题是:

  • 为什么只返回ModificationUserId时Visual Studio会引发类型错误,而在添加更多属性时却抑制该错误?可能是因为name属性已经将其过滤掉了吗?
  • 为什么访问链接表上的name属性在Visual Studio中返回1结果,而在Linqpad中返回3?
  • 在原始的组合示例中,Linqpad是否抛出类型错误,但是Visual Studio过滤建议应用程序之间的执行顺序不同?

我完全意识到可以改进此代码,并且已经做了改进,我只是在寻找一个解释,以说明上面的示例为何执行得与预期的不同。

解决方法

要在LINQPad和Visual Studio中获得相同的行为,必须使用相同的API。

在LINQPad中单击“添加连接”时,您可以选择以下API:LINQ-to-SQL或实体框架(LINQPad 6中的EF Core)。如果您选择LINQ-to-SQL,则如果您想要相同的行为,则还必须在Visual Studio中使用LINQ-to-SQL。而且,如果您选择EF或EF Core,请确保选择的版本与Visual Studio中使用的版本匹配。