Я правильно использую IRepository? - PullRequest
12 голосов
/ 31 июля 2010

Я хочу использовать шаблон IRepository (при поддержке NHibernate, если это имеет значение) в небольшом проекте.Область проста, специально, чтобы я мог сосредоточиться на понимании паттерна IRepository.Класс одиночного домена - Movie со свойствами для Year, Genre и Title.Мое намерение состояло бы в том, чтобы "получить" фильмы, свойства которых соответствуют критериям вышеупомянутых типов.

Конвенция, кажется, имеет общий интерфейс IRepository, подобный следующему:* С базовой реализацией:

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 ]

6 голосов
/ 31 июля 2010
  1. старайтесь не передавать обратно array.используйте IEnumerable<T>, ICollection<T> или IList<T>, это еще больше ослабит ваш код.

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

IMovieRepository : IRepository<Movie> {}

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

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

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

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

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

2 голосов
/ 01 августа 2010

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

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

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

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

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

0 голосов
/ 01 августа 2010

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

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

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

...