延迟执行存储库模式

时间:2014-02-05 06:48:50

标签: c# entity-framework ienumerable repository-pattern iqueryable

在我的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生成的UserTblUser域实体进行映射。

有成千上万的用户,所以我希望GetActive方法在返回IEnumerable<User>时推迟执行,这样就不会不必要地拉出整个列表。我已使用foreachyield完成了上述操作。

测试时,无论如何都会获取所有数据。以下两个调用需要同一时间:

// 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();

我做错了什么?

1 个答案:

答案 0 :(得分:1)

您使用foreach时,会枚举数据。您必须在IQueryable方法之前使用ToList。您使用IEnumerable推迟数据的想法似乎很好,但这是错误的。 IEnumerable始终返回所有数据,它不会强制数据提供程序将其全部保存在内存中。如果要提供者返回数据,则必须使用IQueryable。但是,您不能使用foreachyield,因为它始终枚举其参数中的所有数据。 只做你想要的方法就是将所需的查询传递给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

中的详情