4

我听说在测试 EF 时,您需要对实时数据库使用集成测试,因为 LINQ to Objects 和 LINQ to Entities 提供程序之间存在差异。

为什么我们不能在不实际执行的情况下对查询的构造进行单元测试?我原以为您可以以某种方式将您的 IQueryable 附加到 LINQ to Entities 提供程序并确认 SQL 已正确生成(使用不执行实际查询的 ToTraceString 之类的东西)。

我想像这些行的代码(此查询在 L2O 中运行良好,但在 L2E 中运行良好):

    <Test()> _
    Public Sub Query_Should_Build_Against_L2E()
        Dim testQuery = From d In myDb
                        Where d.Status = CType(Status.Ready, Integer)

        testQuery.SetQueryProvider("L2E")

        Assert.DoesNotThrow(testQuery.ToTraceString())
    End Sub

编辑

我尝试按如下方式实施 GertArnold 的解决方案:

Dim context As New Context("Data Source=fakedbserver;Initial Catalog=fakedb;Persist Security Info=True;")
Dim result = context.myTable.Where(Function(d) d.Status=True)

这会引发一条ProviderIncompatibleException消息“从数据库获取提供程序信息时发生错误。这可能是由 Entity Framework 使用不正确的连接字符串引起的。请检查内部异常以获取详细信息并确保连接字符串正确。” 这是完整的异常链:

System.Data.ProviderIncompatibleException :从数据库获取提供程序信息时出错。这可能是由 Entity Framework 使用不正确的连接字符串引起的。检查内部异常以获取详细信息并确保连接字符串正确。

System.Data.ProviderIncompatibleException :提供程序未返回 ProviderManifestToken 字符串。

System.InvalidOperationException :此操作需要连接到“主”数据库。无法创建到“主”数据库的连接,因为原始数据库连接已打开并且凭据已从连接字符串中删除。提供一个未打开的连接。

System.Data.SqlClient.SqlException :建立与 SQL Server 的连接时发生与网络相关或特定于实例的错误。服务器未找到或无法访问。验证实例名称是否正确以及 SQL Server 是否配置为允许远程连接。(提供者:命名管道提供者,错误:40 - 无法打开与 SQL Server 的连接)

4

2 回答 2

2

将您的 IQueryable 附加到 LINQ to Entities 提供程序

问题是:哪个IQueryable?接口本身什么都不是。使用 EF,您正在处理实现的类IQueryable但除此之外还有更多功能的类。事实上,它们完全具备与上下文和已经附加在引擎盖下的查询提供程序合作的能力。IQueryable.IQueryProvider是一个只读属性,所以它是由创建具体IQueryable实现的工厂设置的。

因此,您的testQuery将是一个,ObjectQuery因为它是从中获取的myDb,除了 EF 上下文之外别无他法。

为什么我们不能对构造进行单元测试在不实际执行进行单元测试?

这不是你的实际问题吗?事实上,我什至想知道您是否想要一个不同的查询提供程序。我认为您会希望 EF 查询提供程序具有这样的行为,否则仍然无法保证使用 EF 它的工作方式相同。

无论如何,您可以在其连接字符串中创建一个带有虚假数据库名称的上下文(以确保它不连接)并检查((ObjectQuery)testQuery).ToTraceString(). 只要你不迭代就可以了testQuery。我可以想象,当查询由复杂的执行路径组成时,这样的测试会有一些价值。(但我宁愿避免这种情况)。

于 2012-07-31T23:20:01.077 回答
0

简短的回答

我能够创建一个模拟 DbContext,它“连接”到正确的 EF 提供程序,而没有任何实际的数据库连接。这保证了被测 LINQ 将在针对您的真实数据库的集成场景中实际工作。它目前仅针对 MS SQL Server 实现,但应该很容易扩展到其他 EF 提供程序。我已经在下面发布了完整的代码,我很高兴听到有关它的反馈。

编辑我将代码移至GPL CodePlex 项目

长答案

在查看了GertArnold的答案并重新思考了整个问题(以及对来源的广泛反思)之后,我想出了我认为可行的解决方案。问题是 DbContext 的默认配置是:

  1. 使用默认创建到构造函数属性SqlConnectionFactory请求的数据库的连接nameOrConnectionStringDbContext
  2. 如果数据库不存在则创建数据库(因为它CreateDatabaseIfNotExists用作默认的上下文初始化程序)
  3. 打开与数据库的连接以确认它是可访问的(使用DbConnection.Openand DbConnection.State
  4. 查询数据库以确认请求的模型存在于其中(使用System.Data.Entity.Internal.InternalContext.QueryForModel它在活动连接上创建一个命令来查询模型的数据库)
  5. 构建一个System.Data.Common.DbProviderServices和一个对象,它们共同为将 Expressions 转换为 SQLSystem.Data.Common.DbProviderManifest提供支持。

这些步骤中的前四个(需要实际的数据库连接)可以创建一个新的,它在方法IDatabaseInitializer中什么都不做,然后使用.InitializeDatabaseDatabase.SetInitializer

另一方面,第 5 步是我们需要一个真正的 DbContext 进行选择的原因。如果没有 DbProviderServices 和 DbProviderManifest,EF 将不知道如何翻译表达式。我能够使用反射来获取内部信息SqlProviderManifest并将SqlProviderServices工作传递给他们(我什至可以指定按年份使用的 SQL Server 版本)。

于 2012-08-08T13:46:44.000 回答