Я правильно использую 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 для любых ошибок, которые будут иметь смысл в вызывающем коде.
Но в целом - это всего лишь мои лакомые кусочки, которые я добавил со временем, чтобы лучше соответствовать своему использованию - и они совершенно необязательны, просто пища для размышлений:-). Я думаю, что вы на правильном пути - я не вижу серьезных проблем в вашем коде.
Надеюсь, что это имеет смысл:-)
стараться не передавать обратно
array
, использованиеIEnumerable<T>
,ICollection<T>
или жеIList<T>
, это будет свободно соединять ваш код дальше.Ваш интерфейс 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
интерфейс...