12

我希望在一个小项目中使用 IRepository 模式(由 NHibernate 支持,如果重要的话)。该域是一个简单的域,有意让我专注于理解 IRepository 模式。唯一的域类是Movie,具有YearGenre和的属性Title。我的意图是“获取”其属性符合上述类型标准的电影。

约定似乎是有一个通用IRepository接口,类似于以下:

public interface IRepository<T>
{
    T Get(int id);
    T[] GetAll();
    void Add(T item);
    void Update(T item);
    void Delete(T item);
}

使用基本实现:

public abstract class Repository<T> : IRepository<T>
{
    public T Get(int id) { ... }
    public T[] GetAll() { ... }
    public void Add(T item) { ... }
    public void Update(T item) { ... }
    public void Delete(T item) { ... }
}

然后要有一个特定于域的接口:

public interface IMovieRepository
{
    Movie[] GetByGenre(Genre genre);
    Movie[] GetByYear(int year);
    Movie[] GetByTitle(string title);
}

使用还扩展基Repository类的实现:

public class MovieRepository : Repository<Movie>, IMovieRepository
{
    public Movie[] GetByGenre(Genre genre) { ... }
    public Movie[] GetByYear(int year) { ... }
    public Movie[] GetByTitle(string title) { ... }
}

我需要使用 NHibernate 为基类和具体的实现添加必要的实现,但我想知道我是否在正确的轨道上使用此设置。

似乎只有一个域类会产生相当多的开销,但如果涉及多个域类,则不会那么明显。现在我正在努力保持简单,以便我可以确定这个概念。

4

3 回答 3

6
  1. 尽量不要传回一个array. 使用IEnumerable<T>, ICollection<T>or IList<T>,这将进一步松散地耦合您的代码。

  2. 你的 IMovieRepository 接口。此存储库包含 CRUD。因此使它

IMovieRepository : IRepository<Movie> {}

这不会更改您的MovieRepository类,因为它将正确实现接口。如果您希望在以后更改实现,它将允许您解耦您的类。

最后。这对于其中一种方法来说很好。由于您具有专门的功能,因此您已经专门针对存储库。

还有其他方法可以让您使用 1 个存储库类并传入所需的查询。这称为规范模式。我做了一个项目,它使用位于 codeplex 上的报告http://whiteboardchat.codeplex.com

另一种方法是有一种方法来传递标准。有一个名为 Sharp Architecture 的开源项目,我相信它已经对其进行了编码。

希望这可以帮助

于 2010-07-31T19:28:18.857 回答
2

我想说,您接近我在运输公司资源规划的生产解决方案中使用的存储库(也使用 NHibernate) - 所以对于初学者来说,我认为您走在正确的道路上。我同意 dbones 使用 IEnumerables /IList 而不是数组 - 你最终会多次编写 .ToArray() :-)。

您可能会考虑以下几点:

优先组合而不是继承 - 而不是从抽象存储库继承 - 让它成为非抽象并将其注入 'ctor 并委托调用 - 这使您的设计在某些情况下更加健壮(例如,对于仅查询存储库等) ) 这样,您还可以选择让抽象存储库可实例化(这是一个词吗?)并控制它是否应该在所有存储库之间共享。

跟进这一点 - 您可能希望将基础存储库更改为具有通用方法,而不是从通用接口继承:

public class Repository
{
    public void Add<T>(T entity)
    {
        using(var session = GetSession())
        using(var tx = session.BeginTransaction())
        {
             session.Save(entity)
             //Transaction handling etc.
        }
    }
    .... //repeat ad nasseum :-)
}

您可能希望让特定的存储库能够访问 ISession - 这极大地提高了您进行查询和控制急切/延迟获取的灵活性,并且您可以充分利用 NHibernate 等。例如

public class Repository
{
    public IList<T> WrapQueryInSession<T>(Func<ISession,IList<T> query)
    {
        using(var session = GetSession())
        using(var tx = session.BeginTransaction())
        {
             var items = query(session);
             //Handle exceptions transacitons etc.
             return items;
        }
     }
 }

用法:

public class MovieRepository : IMovieRepository
{
    private Repository _repository;
    public MovieRepository(Repository repository)
    {
        _repository = repository;
    }
    public IList<Movie> GetByYear(int year)
    {
        Func<ISession, IList<Movie> query = session =>
        {
            var query = session.CreateQuery("from Movie"); //or
            var query = session.CreateCriteria("from Movie"); //or
            var query = session.Linq<Movie>();
            //set criteria etc.
            return query.List<Movie>(); //ToList<Movie>() if you're using Linq2NHibernate
        }:
        return _repository.WrapQueryInSession(query);
    }
}

如果出现问题,您可能还想在您的方法上设置一个 bool 返回值 - 对于调用代码中有意义的任何错误,您可能还需要一个 out IEnumerable。

但总而言之 - 这些只是我随着时间的推移添加的花絮,以更好地符合我的用法 - 它们完全是可选的,只是值得深思的食物:-)。我认为您走在正确的道路上-我在您的代码中看不到任何重大问题。

希望这是有道理的:-)

于 2010-07-31T20:11:59.313 回答
0

值得深思的是,如果您选择的 ORM 有一个 LINQ 提供程序(NH 有一个),您可以尝试使用与集合非常相似的可查询存储库:

public interface IRepository<T> : ICollection<T>, IQueryable<T>

我在我的网站上写了一些关于它的内容:Repository or DAO?: Repository 它与您的构造有相似之处(只是因为集合也支持 CRUD),我尝试的方法意味着您可以拥有不一定知道的代码处理存储库,因为它可以针对ICollectionorIQueryable接口进行编程......

于 2010-07-31T20:43:30.683 回答