在我的UserRepository
我有GetActive
方法:
public IEnumerable<User> GetActive()
{
var users = context.Set<UserTbl>().Where(x => x.IsActive);
foreach(var user in users)
{
yield return entityMapper.CreateFrom(user);
}
}
entityMapper
用于从EF生成的UserTbl
到User
域实体进行映射。
有成千上万的用户,所以我希望GetActive
方法在返回IEnumerable<User>
时推迟执行,这样就不会不必要地拉出整个列表。我已使用foreach
和yield
完成了上述操作。
测试时,无论如何都会获取所有数据。以下两个调用需要同一时间:
// Get only 5 users in memory
var someUsers = UserRepository.GetActive().Take(5).ToList();
// Get all 100,000 users into memory
var allUsers = UserRepository.GetActive().ToList();
我做错了什么?
答案 0 :(得分:1)
您使用foreach
时,会枚举数据。您必须在IQueryable
方法之前使用ToList
。您使用IEnumerable
推迟数据的想法似乎很好,但这是错误的。 IEnumerable
始终返回所有数据,它不会强制数据提供程序将其全部保存在内存中。如果要提供者返回数据,则必须使用IQueryable
。但是,您不能使用foreach
和yield
,因为它始终枚举其参数中的所有数据。
只做你想要的方法就是将所需的查询传递给GetActive
方法。
public IEnumerable<User> GetActive(Func<IQueryable<User>, IQueryable<User>> modifier)
{
var users = modifier(context.Set<UserTbl>().Where(x => x.IsActive));
foreach(var user in users)
{
yield return entityMapper.CreateFrom(user);
}
}
// Get only 5 users in memory
var someUsers = UserRepository.GetActive(q=>q.Take(5)).ToList();
// Get all 100,000 users into memory
var allUsers = UserRepository.GetActive(q=>q).ToList();
但我真的建议你的架构中根本没有存储库。它们在已经复杂的ORM中引入了不必要的复杂性。请参阅Repository pattern with Entity framework
中的详情