Я правильно использую 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, но я хотел бы знать, правильно ли я нахожусь на этом пути.
Похоже, что для одного класса домена существует довольно много накладных расходов, хотя было бы менее заметно, если бы было задействовано несколько классов доменов. Сейчас я стараюсь, чтобы это было просто, поэтому я могу определить концепцию.
Ответы
Ответ 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
интерфейс...