Подтвердить что ты не робот

Я правильно использую 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, но я хотел бы знать, правильно ли я нахожусь на этом пути.

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

4b9b3361

Ответ 1

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

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

Преимущество Композиция над наследованием - вместо наследования из абстрактного репозитория - пусть она не абстрактна и вводит ее в "ctor" и делегирует вызовы - это делает ваш дизайн более надежным в определенных ситуациях (например, для Query-only репозиторий и т.д.) Таким образом, у вас также есть возможность позволить абстрактному репозиторию быть способным (это слово?) и контролировать, следует ли использовать его во всех репозиториях.

Следуя этому вопросу, вы можете захотеть изменить базовый репозиторий на наличие общих методов вместо наследования от общего интерфейса:

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

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

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

IMovieRepository : IRepository<Movie> {}

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

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

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

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

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

Ответ 3

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

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

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