单元测试错误:此函数只能从 LINQ 调用到实体

Posted

技术标签:

【中文标题】单元测试错误:此函数只能从 LINQ 调用到实体【英文标题】:Unit test error : This function can only be invoked from LINQ to Entities 【发布时间】:2015-04-03 10:14:47 【问题描述】:

我正在编写一个 MVC 5 互联网应用程序,我的表达式如下:

public Expression<Func<Account, bool>> IsExpiresDateTimeLessThanMinimumDaysLeftInFreeTrialSubscription(int minimumDaysLeftInSubscriptionForEmail)

    return Account => System.Data.Entity.DbFunctions.DiffHours(Account.freeTrialEndDate, DateTime.UtcNow) < minimumDaysLeftInSubscriptionForEmail;

从数据库中检索数据时,上述表达式正确完成。但是,在编写使用上述表达式的单元测试时,出现以下错误:

此函数只能从 LINQ to Entities 调用

我推测这是因为System.Data.Entity.DbFunctions.DiffHours 函数将expression 转换为只有数据库系统才能理解的代码。

由于上述事实,当使用使用List 而不是DbSet 的模拟存储库时,是否可以对上述表达式进行单元测试?如果没有,我应该如何对使用expression 的任何代码进行单元测试?是否可以对expression 进行单元测试?

提前致谢。

【问题讨论】:

【参考方案1】:

当 EF 生成 SQL 代码时,唯一重要的是 System.Data.Entity.DbFunction 属性。方法体抛出异常,但在使用真实数据库时从不调用。

要对此进行测试,您可以使用DbFunction 属性和实现创建自己的方法。针对数据库运行时,EF 将生成 SQL 并忽略您的代码。运行单元测试时,您的代码将被执行。

你的方法看起来像这样:

public static class TestableDbFunctions

    [System.Data.Entity.DbFunction("Edm", "DiffHours")]
    public static int? DiffHours(DateTime? dateValue1, DateTime? dateValue2)
    
        if (!dateValue1.HasValue || !dateValue2.HasValue)
            return null;

        return (int)((dateValue2.Value - dateValue1.Value).TotalHours);
    

比较代码只是举例,您需要确保它与 SQL 行为匹配,否则您的测试将无效。

一旦你有了这个,只需更改你的代码以使用新方法:

return Account => TestableDbFunctions.DiffHours(Account.freeTrialEndDate, DateTime.UtcNow) < minimumDaysLeftInSubscriptionForEmail;

如果您为此编写了一个好的测试,它将捕获您在几天内通过并比较几个小时的错误。

【讨论】:

上面的代码是否自动判断代码是针对数据库还是列表运行?如果没有,可以将其编码到函数中吗?我问这个是因为表达式是 where 子句中的许多表达式之一。 它将自动针对数据库或列表运行正确的行为。实际的确定是通过 IQueryable 的实现来完成的。 List.AsQueryable() 将表达式树解释为 LINQ to 对象,并针对内存中的列表运行 C# 代码。 ObjectSet 实现将解释表达式树以生成 SQL,使用 DbFunction 属性并忽略 C# 代码。 对于SqlFunctions.DatePart(),我使用了属性[DbFunction("SqlServer", "DATEPART")](注意故意大写的第二个参数以精确匹配this value)。

以上是关于单元测试错误:此函数只能从 LINQ 调用到实体的主要内容,如果未能解决你的问题,请参考以下文章

从休眠实体生成数据库

Linq 错误:“where”条件无效。实体成员正在调用无效的属性或方法

选择中的Linq到实体自定义功能

运行第二个单元测试时核心数据实体名称为零

ReactWrapper::state() 只能在类组件单元测试 Jest 和 Enzyme 上调用

错误:在 null 上调用成员函数 create(),单元测试(嘲弄)