我正确使用IRepository吗?

Gra*_*lin 12 c# design-patterns irepository

我想在一个小项目中使用IRepository模式(由NHibernate支持,如果它很重要).域是一个简单的域,故意让我专注于理解IRepository模式.独行域类是Movie,与性质Year,GenreTitle.我的意图是"获取"其属性符合上述类型标准的电影.

惯例似乎是具有通用IRepository接口,类似于以下内容:

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

有了基础实现:

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) { ... }
}
Run Code Online (Sandbox Code Playgroud)

然后有一个特定于域的界面:

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

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

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

我需要使用NHibernate添加必要的实现到基类以及具体的实现,但我想知道我是否在这个设置的正确轨道上.

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

dbo*_*nes 6

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

  2. 你的IMovieRepository接口.此存储库包括CRUD.因此成功

IMovieRepository : IRepository<Movie> {}

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

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

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

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

希望这可以帮助


Gob*_*lin 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 :-)
}
Run Code Online (Sandbox Code Playgroud)

您可能想让特定的存储库能够访问 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;
        }
     }
 }
Run Code Online (Sandbox Code Playgroud)

用法:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

希望这是有道理的:-)