作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我目前正在为MVC4应用程序中的存储库实现编写单元测试。为了模拟数据上下文,我首先采用了this post的一些想法,但是现在我发现了一些局限性,这些疑问使我怀疑是否有可能正确模拟IQueryable
。
特别是,在某些情况下,我通过了测试,但是代码在生产中失败了,并且我无法找到任何方法来模拟导致此失败的行为。
例如,以下代码段用于选择属于预定义类别列表的Post
实体:
var posts = repository.GetEntities<Post>(); // Returns IQueryable<Post>
var categories = GetCategoriesInGroup("Post"); // Returns a fixed list of type Category
var filtered = posts.Where(p => categories.Any(c => c.Name == p.Category)).ToList();
posts
实现来模拟
DbSet
,还尝试通过创建
List
实例的
Post
并使用
IQueryable
扩展方法将其转换为
AsQueryable()
来进行模拟。这两种方法都可以在测试条件下工作,但是代码实际上在生产中会失败,但以下情况除外:
System.NotSupportedException : Unable to create a constant value of type 'Category'. Only primitive types or enumeration types are supported in this context.
IQueryable
实现的行为是否不切实际?
最佳答案
我认为模拟 Entity Framework 的行为非常困难,甚至不可能。首先也是最重要的是,因为这需要对linq-entent与linq-to-objects不同的所有特性和边缘情况的深入了解。正如您所说:真正的挑战是找到它们。让我指出三个主要 Realm ,而不是声称自己几乎是详尽无遗的:
Linq到对象成功而Linq到实体失败的情况:
.Select(x => x.Property1.ToString()
。 LINQ to Entities无法识别方法'System.String ToString()'方法...这几乎适用于本机.Net类中的几乎所有方法,当然也适用于自己的方法。只有少数.Net方法将转换为SQL。参见CLR Method to Canonical Function Mapping。从EF 6.1开始,该方式支持ToString
。但是只有无参数过载。 Skip()
的OrderBy
。 Except
和Intersect
:可能会产生令人讨厌的查询,从而引发您的SQL语句的某些部分嵌套得太深。重写查询或将其分解为较小的查询。 Select(x => x.Date1 - x.Date2)
:DbArithmeticExpression参数必须具有数字通用类型。 .Where(p => p.Category == category)
:在此上下文中仅支持原始类型或枚举类型。 Nodes.Where(n => n.ParentNodes.First().Id == 1)
:方法“First”只能用作最终查询操作。 context.Nodes.Last()
:LINQ to Entities无法识别方法'... Last ...'。这适用于许多其他IQueryable
扩展方法。参见Supported and Unsupported LINQ Methods。 .Select(x => new A { Property1 = (x.BoolProperty ? new B { BProp1 = x.Prop1, BProp2 = x.Prop2 } : new B { BProp1 = x.Prop1 }) })
:类型B在here的单个LINQ to Entities查询中出现在两个结构不兼容的初始化中。 context.Entities.Cast<IEntity>()
:无法将类型“Entity”强制转换为类型“IEntity”。 LINQ to Entities仅支持强制转换EDM基本类型或枚举类型。 .Select(p => p.Category?.Name)
。在表达式中使用空传播会引发CS8072表达式树lambda可能不包含空传播运算符。这may get fixed one day。 .Select(p => p.Category.Name)
:当p.Category
为null时,L2E返回null,但是L2O抛出未设置为对象实例的Object reference。这不能通过使用空传播来解决(请参见上文)。 Nodes.Max(n => n.ParentId.Value)
,其中n.ParentId
具有一些空值。 L2E返回一个最大值,L2O引发Nullable对象必须具有一个值。 EntityFunctions
(从EF 6开始为DbFunctions
)或SqlFunctions
。 Nodes.Include("ParentNodes")
:L2O没有包含的实现。它将运行并返回节点(如果Nodes
是IQueryable
),但没有父节点。 Nodes.Select(n => n.ParentNodes.Max(p => p.Id))
集合的
ParentNodes
:都失败,但有不同的异常(exception)。 Nodes.Where(n => n.Name.Contains("par"))
:L2O区分大小写,L2E取决于数据库排序规则(通常不区分大小写)。 node.ParentNode = parentNode
:具有双向关系,在L2E中,还将节点添加到父节点的节点集合中(关系修正)。不在L2O中。 (请参阅Unit testing a two way EF relationship)。 .Select(p => p.Category == null ? string.Empty : p.Category.Name)
:结果相同,但是生成的SQL查询还包含空检查,可能难以优化。 Nodes.AsNoTracking().Select(n => n.ParentNode
。这个非常棘手! 。使用AsNoTracking
,EF会为每个ParentNode
创建新的Node
对象,因此可以有重复项。没有AsNoTracking
,EF会重复使用现有的ParentNodes
,因为现在涉及到实体状态管理器和实体密钥。可以在L2O中调用AsNoTracking()
,但它不会执行任何操作,因此,无论有没有它,都不会有任何区别。 关于entity-framework - 如何模拟EntityFramework的IQueryable实现的局限性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13332002/
我想做如下的事情: class Foo(object): def __init__(self): self.member = 10 pass def facto
我是一名优秀的程序员,十分优秀!