当伪造数据库时,FirstOrDefault返回null

本文关键字:FirstOrDefault 返回 null 伪造 数据库 | 更新日期: 2023-09-27 18:02:23

我试图为我的项目创建一些单元测试,经过大量挖掘,我发现努力,这个想法是伟大的,它模拟数据库而不是处理伪造DBContext,顺便说一下,在使用复杂的模式时很难得到正确的。

然而,我试图得到一个用户的电子邮件后,我特别将其添加到由努力创建的内存数据库,这里是代码

MyContext contextx = new MyContext(Effort.DbConnectionFactory.CreateTransient());
var client = new Client
{
    ClientId = 2,
    PersonId = 3,
    Person = new Person
    {
        PersonId = 3,
        EMail = "xxxxx@gmail.com"
    }
};
contextx.Client.Add(client); //<-- client got added, I checked it and is there
var email = contextx.Client.Select(c => c.Person.EMail).FirstOrDefault(); 

在上面的最后一行中,我不能使它返回电子邮件xxxx@gmail.com,而是它总是返回null。

任何想法?

当伪造数据库时,FirstOrDefault返回null

直接回答问题

对于你提出的具体问题,我有两点建议:

  1. 查看contextx.Client.ToArray(),看看该集合中真正有多少成员。可能Client集合实际上是空的,在这种情况下,您确实会得到null。或者,它可能是Client集合中的第一个元素对于EMail具有空值。

  2. 如果在DbContext上查询Client集合之前调用contextx.SaveChanges(),行为会如何变化?我很想知道调用SaveChanges是否会导致新插入的值存在于集合中。这真的不应该是必需的,但是在Effort和DbContext之间可能会有一些奇怪的相互作用。

EDIT: SaveChanges()原来是答案。

一般测试建议

既然你给这个问题加上了"单元测试"标签,我将根据我作为单元测试实践者和教练的十年经验,提供一些一般的单元测试建议。单元测试是关于隔离地测试应用程序的各个小部分。这通常意味着单元测试一次只与几个类交互。这也意味着单元测试不应该依赖于外部库或依赖项(比如数据库)。相反,一个集成测试同时测试系统的更多部分,并且可能有外部依赖,比如数据库。

虽然这看起来像是对术语的吹毛求疵,但术语对于向团队的其他成员传达测试的实际意图是很重要的。

在这种情况下,要么你真的想要单元测试一些碰巧依赖于DbContext的功能,要么你正在尝试测试你的数据访问层。如果您试图为直接依赖于DbContext的东西编写一个独立的单元测试,那么您需要打破对DbContext的依赖。我将在下面的中打破对DbContext的依赖来解释这一点。否则,您实际上是在尝试集成测试DbContext,包括实体是如何映射的。在这种情况下,我总是发现最好隔离这些测试并使用真正的(本地)数据库。您可能希望使用与生产中使用的数据库类型相同的本地安装数据库。通常,SqlExpress工作得很好。将测试指向测试可以完全废弃的数据库实例。让您的测试在运行每个测试之前删除所有现有数据。然后,他们可以设置他们需要的任何数据,而不用担心现有的数据会冲突。

打破对DbContext的依赖

那么,当您的业务逻辑依赖于访问DbContext时,您如何编写好的单元测试?你不。

在我使用实体框架进行数据持久化的应用程序中,我确保对DbContext的访问包含在单独的数据访问项目中。通常,我将创建实现Repository模式的类,并且允许这些类依赖于DbContext。因此,在本例中,我将创建实现IClientRepository接口的ClientRepository。接口看起来像这样:

public interface IClientRepository {
    Client GetClientByEMail(string email);
}

然后,任何需要访问该方法的类都可以使用基本的存根/mock/任何东西进行单元测试。不用担心模仿DbContext。包含了数据访问层,您可以使用真实的数据库对其进行彻底测试。有关如何测试数据访问层的一些建议,请参阅上文。

作为一个额外的好处,这个接口的实现定义了在一个单一的、统一的地方通过电子邮件地址查找Client的含义。IClientRepository接口允许您快速回答问题,"我们如何查询系统中的Client实体?"

DbContext的依赖与允许域类对连接字符串的依赖和ADO的测试问题的规模大致相同。Net代码无处不在。这意味着您必须创建一个包含真实数据的真实数据存储(甚至使用假数据库)。但是,如果您将对DbContext的访问包含在特定的数据访问程序集中,您会发现编写单元测试要容易得多。

就项目组织而言,我通常只允许我的数据访问项目引用实体框架。我将有一个单独的Core项目,我在其中定义实体。我还将在Core项目中定义数据访问接口。然后,将具体的接口实现放到数据访问项目中。在你的解决方案中,大多数项目都可以简单地依赖于核心项目,只有顶级的可执行或web项目真正需要依赖于数据访问项目。