我正确使用IRepository吗?

时间:2010-07-31 19:02:39

标签: c# design-patterns irepository

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

惯例似乎是具有通用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将必要的实现添加到基类以及具体的实现,但我想知道我是否在使用此设置进入正确的轨道。

对于一个域类似乎有相当大的开销,但如果涉及多个域类则不太明显。现在我正试图保持简单,所以我可以确定这个概念。

3 个答案:

答案 0 :(得分:6)

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

  2. 您的IMovieRepository界面。此存储库包括CRUD。因此,

  3. IMovieRepository : IRepository<Movie> {}

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

    最后。这对其中一种方法来说很好。因为你有专门的功能,所以你专门设置了适合的存储库。

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

    另一种方法是有一个传递标准的方法。有一个名为Sharp Architecture的开源项目,我认为这个编码了。

    希望这有帮助

答案 1 :(得分:2)

我会说,您接近我在运输公司的资源规划生产解决方案中使用的存储库(也使用NHibernate) - 所以对于初学者来说,我认为你走的是正确的道路。我同意使用IEnumerables / IList而不是数组的dbones - 你最终会多次编写.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返回值 - 对于在调用代码中有意义的任何错误,可能需要输出IEnumerable。

但总而言之 - 这些只是我随着时间的推移而添加的花絮,以更好地遵守我的用法 - 而且它们完全是可选的,只是值得思考的食物:-)。我认为你走的是正确的道路 - 我认为你的代码中没有任何重大问题。

希望这是有道理的: - )

答案 2 :(得分:0)

作为思考的食物,如果您选择的ORM有一个LINQ提供者(而NH有一个),您可以尝试与集合非常相似的可查询存储库:

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

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