Я правильно использую IRepository?

Я хочу использовать шаблон IRepository (при поддержке NHibernate, если это имеет значение) в небольшом проекте. Область проста, специально, чтобы я мог сосредоточиться на понимании паттерна IRepository. Класс одинокого домена Movie, со свойствами для Year, Genre, а также 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, но я хотел бы знать, нахожусь ли я на правильном пути с этой настройкой.

Похоже, что для одного класса домена излишние издержки, но это было бы менее заметно, если бы было задействовано несколько классов домена. Прямо сейчас я пытаюсь сделать это простым, чтобы я мог закрепить концепцию.

3 ответа

Решение

Я бы сказал, что вы близки к хранилищу, которое я использую в производственном решении для планирования ресурсов в транспортных компаниях (в том числе и с помощью NHibernate) - так что для начала вы, по моему мнению, на правильном пути. Я согласен с dbones в использовании IEnumerables /IList вместо массивов - в конечном итоге вы будете писать.ToArray() много раз:-).

Несколько вещей, которые вы могли бы рассмотреть:

Композиция Favor вместо наследования - вместо наследования от абстрактного репозитория - пусть она будет неабстрактной и внедрять ее в 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 для любых ошибок, которые будут иметь смысл в вызывающем коде.

Но в целом - это всего лишь мои лакомые кусочки, которые я добавил со временем, чтобы лучше соответствовать своему использованию - и они совершенно необязательны, просто пища для размышлений:-). Я думаю, что вы на правильном пути - я не вижу серьезных проблем в вашем коде.

Надеюсь, что это имеет смысл:-)

  1. стараться не передавать обратно array, использование IEnumerable<T>, ICollection<T> или же IList<T>, это будет свободно соединять ваш код дальше.

  2. Ваш интерфейс IMovieRepository. этот репозиторий включает в себя CRUD. поэтому сделай это

IMovieRepository : IRepository<Movie> {}

Это не изменит ваш MovieRepository класс как таковой будет правильно реализовывать интерфейс. это позволит вам разъединить ваши классы, если вы захотите изменить реализацию позднее.

в конце концов. это хорошо для одного из методов. поскольку у вас есть специализированные функциональные возможности, у вас есть подходящее хранилище.

Существуют и другие способы, которые позволяют вам использовать 1 класс хранилища и передать требуемый запрос. Это называется шаблоном спецификации. Я сделал проект, который использует это, расположенный на codeplex с отчетом http://whiteboardchat.codeplex.com/

Другой способ - использовать метод для передачи критериев. есть проект с открытым исходным кодом под названием Sharp Architecture, который, я думаю, уже закодирован.

Надеюсь это поможет

В качестве пищи для размышления, если у вашего ORM по выбору есть поставщик LINQ (а у NH - один), вы можете попробовать запрашиваемый репозиторий, который очень похож на коллекцию:

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

Я написал кое-что об этом на своем сайте: Репозиторий или DAO?: Репозиторий Он имеет сходство с вашей конструкцией (только потому, что коллекция поддерживает CRUD), подход, который я пробую, означает, что вы можете иметь код, который не обязательно знает имея дело с хранилищем, так как он может быть запрограммирован против ICollection или же IQueryable интерфейс...

Другие вопросы по тегам