存储库实现

时间:2013-02-27 16:35:16

标签: nhibernate repository

我正在考虑改进我当前的存储库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. 不再需要创建自定义Repos。单一的通用就足够了。
  2. 现在,自定义规范实现与数据层分离并且可以测试。
  3. 易于使用的服务。
  4. 我们始终能够扩展QueryWrapper以支持其他方法,例如OrderBy等。
  5. 它仍然是单元可测试的。
  6. 请您指出我的方法泄漏或提出您对问题的看法?此外,现有文章的链接也很棒。

1 个答案:

答案 0 :(得分: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窗体项目中使用存储库进行测试。