【问题标题】:Should repositories implement IQueryable<T>?存储库是否应该实现 IQueryable<T>?
【发布时间】:2008-10-02 20:13:27
【问题描述】:

我正在考虑两个 IRepository 接口之一,一个是 IQueryable 的后代,一个包含 IQueryable。

像这样:

public interface IRepository<T> : IQueryable<T>
{
    T Save(T entity);
    void Delete(T entity);
}

或者这个:

public interface IRepository<T>
{
    T Save(T entity);
    void Delete(T entity);
    IQueryable<T> Query();
}

LINQ 的用法是:

from dos
in ServiceLocator.Current.GetInstance<IRepository<DomainObject>>()
where dos.Id == id
select dos

或者……

from dos
in ServiceLocator.Current.GetInstance<IRepository<DomainObject>>().Query
where dos.Id == id
select dos

我有点喜欢第一个,但模拟起来有问题。其他人是如何实现 LINQable、mockable 存储库的?

【问题讨论】:

  • 只是为了确认?如果您在 Query() 中返回 all 实体,然后在消费代码中使用 linq 进行搜索,它会延迟加载,对吗?它实际上不会加载数千条记录,然后在您的消费代码中搜索它们?

标签: .net linq design-patterns


【解决方案1】:

取决于您想要 Has-A 还是 Is-A 关系。

第一个是 Is-A 关系。 IRepository 接口是一个 IQueryable 接口。第二个是has-a。 IRepository 有一个 IQueryable 接口。在写这篇文章的过程中,我实际上比第一个更喜欢第二个,只是因为当使用你的第二个 IRepository 时,我可以给 Query() 方法任何返回 IQueryable 的方法。对我来说,这比第一个实现更灵活。

【讨论】:

  • 这就是我最终做的事情。模拟示例 1 实在是太痛苦了,反正起订量也是如此。
  • 相关问题,我正在使用 Moq 模拟示例 2 中的存储库,但我的查询在“select new { ... }”中包含映射逻辑,该逻辑从未得到测试,因为执行被推迟到IRepository.Save(),它被模拟并且从不执​​行映射。有什么想法吗?
【解决方案2】:

就个人而言,我使用Repository Pattern 将存储库中的所有项目作为IQueryable 返回。通过这样做,我的存储库层现在变得非常轻、小......服务层(它消耗存储库层)现在可以对所有类型的查询操作开放。

基本上,我所有的逻辑现在都位于服务层(它不知道它将使用什么类型的存储库......并且不想知道

例如。或多或少的伪代码,因为我记得我们在代码中做了什么并为这个答案简化了它......

public interface IRepository<T>
{
    IQueryable<T> Find();
    void Save(T entity);
    void Delete(T entity);
}

并拥有一个用户存储库...

public class UserRepository : IRepository<User>
{
    public IQueryable<User> Find()
    {
        // Context is some Entity Framework context or 
        // Linq-to-Sql or NHib or an Xml file, etc...
        // I didn't bother adding this, to this example code.
        return context.Users().AsQueryable();
    }

    // ... etc
}

现在是最好的一点:)

public void UserServices : IUserServices
{
    private readonly IRepository<User> _userRepository;

    public UserServices(IRepository<User> userRepository)
    {
        _userRepository = userRepository;
    }

    public User FindById(int userId)
    {
        return _userRepository.Find()
            .WithUserId(userId)
            .SingleOrDefault();  // <-- This will be null, if the 
                                 //     user doesn't exist
                                 //     in the repository.
    }

    // Note: some people might not want the FindBySingle method because this
    //       uber method can do that, also. But i wanted to show u the power
    //       of having the Repository return an IQuerable.
    public User FindSingle(Expression<Func<User, bool>> predicate)
    {
        return _userRepository
            .Find()
            .SingleOrDefault(predicate);
    }
}

奖励积分:FindById 方法中的 WTF 是 WithUserId(userId)?那是Pipe and Filter。使用它们 :) 爱它们 :) 拥抱它们 :) 它们使您的代码非常易读 :) 现在,如果您想知道它的作用.. 这是扩展方法。

public static User WithId(this IQueryable<User> source, int userId)
{
    return source.Where(u => u.UserId == userId).SingleOrDefault();
}

尽管这个问题是 .. 嗯... 快两年了 :)

【讨论】:

  • 我喜欢管道和过滤器的尖端
【解决方案3】:

您总是可以针对 List 快速编写内容,它不是使用模拟框架进行模拟,但它确实很有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-09
    • 2013-10-14
    • 2011-05-24
    • 2021-12-12
    • 2015-07-20
    • 1970-01-01
    • 2011-03-03
    相关资源
    最近更新 更多