3

我正在考虑改进我当前的存储库GetAll方法实现并在下面编写了代码。我试图在谷歌中找到这种方法的想法,但没有成功。因此,请查看代码并帮助我回答以下一些问题。这是简化的示例:

class Service
{
    public void DoOperation()
    {
        // Let's say we need to retrieve users by criteria
        var items = UnitOfWork
            .Over<User>() // Get repository of User
            .GetAll()     // Start querying

            // If we need simple WHERE then use:
            .Where(x => x.Email == "some@email.com")

            // If we need more complex condition then use specification:
            .Using(new UserNameContains("John"))

            // Execute:
            .List();
    }
}

class UnitOfWork
{
    public Repository<T> Over<T>()
    {
        // Get from DI container or injected field
        return new Repository<T>();
    }
}

class Repository<T>
{
    public QueryWrapper<T> GetAll()
    {
        return new QueryWrapper<T>(Session.QueryOver<T>());
    }
}

class QueryWrapper<T>
{
    // Query from DB session. Init from constructor.
    private IQueryOver<T> _query;

    public QueryWrapper<T> Where(Expression<Func<T, bool>> expression)
    {
        _query = _query.Where(expression);
        return this;
    }

    public QueryWrapper<T> Using(Specification<T> spec)
    {
        var spec = new TSpec();
        _query = spec.Apply(_query);
        return this;
    }

    public IEnumerable<T> List()
    {
        return return _query.List();
    }
}

abstract class Specification<T>
{
    public abstract IQueryOver<T> Apply(IQueryOver<T> query);
}

class UserNameContains : Specification<User>
{
    // Init from constructor:
    private string _name;

    public override IQueryOver<User> Apply(IQueryOver<User> query)
    {
        return /* apply filter condition here */;
    }
}

因此,我们得到了这样的好处:

  1. 不再需要创建自定义回购。单个泛型就足够了。
  2. 自定义规范实现现在与数据层分离并且可测试。
  3. 易于使用的服务。
  4. 我们总是能够扩展QueryWrapper以支持其他方法,例如OrderBy等。
  5. 它仍然是可单元测试的。

您能否指出我的方法的漏洞或提供您对问题的看法?现有文章的链接也很棒。

4

1 回答 1

1

我的建议是将您的架构减少到最低限度,并向自己证明每个添加层和抽象的好处。最小的 GetAll 实现是:

Session.Query<User>();

如果您有将被重复使用的限制,您可以将它们添加为扩展方法。如果可重用代码变得明显,那么重构为扩展方法就足够简单了。即使这样,对于简单的情况,如果您只包装一个 Lambda 表达式,如本例所示,重构通常没有什么好处。

public static IQueryable<User> UserNameContains(this IQueryable<User> queryable, string text)
{
    return queryable.Where(u => u.UserNameContains(text));
}

我发现存储库模式不是很有用,但它可能是一种组织代码的合理方式。但我真的不喜欢通用存储库模式,因为它强制每个类规则一个存储库。如果我使用存储库,我喜欢将与根对象(例如 Project、ProjectStatus 等)相关的查询分组。此外,在通用存储库中提供 Get 或 GetAll 方法没有任何好处,因为 ISession 已经提供了这些方法。

至于可测试性,查询测试始终是集成测试,在 ASP.NET MVC 中,我直接测试控制器或任何独立的查询方法。我在 Windows 窗体项目中使用存储库进行测试。

于 2013-03-01T14:43:18.817 回答