Я надеюсь использовать шаблон 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, но я хотел бы знать, на правильном пути ли я с этой установкой.
Кажется, существует немного издержек всего для одного доменного класса, хотя было бы менее примечательно, если бы было несколько доменных включенных классов. Прямо сейчас я пытаюсь сохранить это простым, таким образом, я могу придавить понятие.
Я бы сказал, вы близки к репозиторию, который я использую в производственном решении для планирования ресурсов в транспортных компаниях. (также используя NHibernate) - так что для начала, на мой взгляд, вы на правильном пути. Я согласен с dbones на использование IEnumerables / IList вместо массивов - вы в конечном итоге напишете .ToArray () много раз :-).
Несколько вещей, которые вы могли бы рассмотреть:
Предпочитайте композицию наследованию - вместо наследования из абстрактного репозитория - пусть она будет не абстрактной, и вставьте ее в '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 для любых ошибок, которые будут иметь смысл в вызывающем коде.
Но в целом - это всего лишь мои лакомые кусочки, которые я добавил со временем, чтобы лучше соответствовать моему использованию - и они совершенно необязательны, просто пища для размышлений :-). Думаю, вы на правильном пути - я не вижу серьезных проблем в вашем коде.
Надеюсь, это имеет смысл: -)
постарайтесь не передавать обратно массив
. используйте IEnumerable
, ICollection
или IList
, это еще больше ослабит ваш код.
ваш интерфейс IMovieRepository. этот репозиторий включает CRUD. поэтому сделайте это
IMovieRepository: IRepository
Это не изменит ваш класс MovieRepository
, так как это будет правильно реализовывать интерфейс. это позволит вам отделить классы, если вы захотите изменить реализацию позже.
наконец. это нормально для одного из методов. поскольку у вас есть специализированная функциональность, вы настроили репозиторий в соответствии с требованиями.
есть и другие способы, которые позволяют использовать 1 класс репозитория и передавать требуемый запрос. Это называется шаблоном спецификации. Я сделал проект, который использует это, расположенное на codeplex, с отчетом http://whiteboardchat.codeplex.com
, иначе можно было бы получить метод для передачи критериев. есть проект с открытым исходным кодом под названием Sharp Architecture, в котором, как мне кажется, это закодировано.
Надеюсь, это поможет
В качестве пищи для размышлений, если ваш ORM по выбору имеет поставщика LINQ (а у NH есть один), вы можете попробовать запросить репозиторий, который очень похож на коллекцию:
public interface IRepository<T> : ICollection<T>, IQueryable<T>
Я написал кое-что об этом на моем сайте: Репозиторий или DAO ?: Репозиторий
Он имеет сходство с вашей конструкцией (просто потому, что коллекция также поддерживает CRUD), подход, который я пробую, означает, что у вас может быть код, который не обязательно знает о работе с репозиторием, поскольку его можно запрограммировать на ICollection
или интерфейс IQueryable
...