Ця стаття має кілька недоліків. Будь ласка, допоможіть удосконалити її або обговоріть ці проблеми на сторінці обговорення.
Ця стаття не має інтервікі-посилань. Ви можете допомогти проєкту, знайшовши та додавши їх до відповідного елементу Вікіданих.(15 червня 2019)
Ця стаття містить перелік посилань, але походження тверджень у ній залишається незрозумілим через практично повну відсутність внутрішньотекстових джерел-виносок. Будь ласка, допоможіть поліпшити цю статтю, перетворивши джерела з переліку посилань на джерела-виноски у самому тексті статті.(15 червня 2019)
Вступний розділ цієї статті, ймовірно, несповна підсумовує ключові тези її вмісту. Будь ласка, допоможіть розширити вступ, додавши стислий огляд найважливіших аспектів статті.(15 червня 2019)
UML діаграма, що описує структуру шаблону проєктування Repository
Repository — патерн, який розділяє рівні джерела даних і логіки програми. Часто використовується із патерном Unit Of Work
Зміст
1Переваги та недоліки
1.1Переваги
1.2Недоліки
2Опис мовою C#
3Зв'язок з іншими патернами
4Реалізація
5Див. також
6Джерела
Переваги та недоліки
Цей розділ має вигляд переліку, який краще подати прозою. Ви можете допомогти викласти список прозою, де це доречно. Ознайомтеся з довідкою з редагування.(15 червня 2019)
Переваги
Використовується, як колекція
Інкапсулює великі запити до БД в методи
Рівень абстракції між Business Logic рівнем та Data Access рівнем
Ізолює програму від змін джерела даних
Джерело даних може бути змінено без будь-яких змін в бізнес логіці і з мінімальними змінами в Репозиторії
Полегшує автоматизоване юніт тестування, Test Driven Development
Легко створювати mock репозиторію
Недоліки
Зростає кількість класів
Погіршує продуктивність
Обмежує у використанні особливостей ОРМ фреймворку
Опис мовою C#
Використаємо Entity Framework. Нехай дано клас-сутність User
Тепер напишемо інтерфейс репозиторію. Варто зазначити, що репозиторій є колекцією, і має поводитись як колекція, та не містити методів Update(), Save() тощо. Також однією із великих помилок, є те що методи повертають IQueryable замість IEnumerable. Якщо повертати IQueryable це дозволить надбудувати над запитом, ще запити, що не є вірним, оскільки мета цього патерну якраз і є уникнення великих запитів. В такому разі, краще написати ще один метод, який буде виконувати більший запит.
Тепер реалізуємо цей інтерфейс у вигляді узагальненого класу. При реалізації ми повертаємо сутність, а не DTO. Мапування — це не відповідальність репозиторію.
publicclassGenericRepository<TEntity>:IRepository<TEntity>whereTEntity:class{// FIELDS// узагальнений контекстprotectedDbContextdbContext;protectedDbSet<TEntity>dbSet;// CONSTRUCTORSpublicGenericRepository(DbContextdbContext){this.dbContext=dbContext;this.dbSet=dbContext.Set<TEntity>();}// METHODSpublicvirtualintCount(){returndbSet.Count();}publicvirtualintCount(Expression<Func<TEntity,bool>>predicate){returndbSet.Count(predicate);}publicvirtualIEnumerable<TEntity>Get(Expression<Func<TEntity,bool>>filter=null,Func<IQueryable<TEntity>,IOrderedQueryable<TEntity>>orderBy=null,stringincludeProperties="",int?page=null,int?amount=null){// filterIQueryable<TEntity>query=dbSet;if(filter!=null){query=query.Where(filter);}// include propertiesforeach(stringincludePropertyinincludeProperties.Split(newchar[]{',',' '},StringSplitOptions.RemoveEmptyEntries)){query=query.Include(includeProperty);}// orderingif(orderBy!=null)query=orderBy(query);// pagingif(page.HasValue&&amount.HasValue)query=query.Skip((page.Value-1)*amount.Value).Take(amount.Value);returnquery;}publicvirtualTEntityGet(intid){returndbSet.Find(id);}publicvirtualvoidInsert(TEntityentity){dbSet.Add(entity);}publicvirtualvoidDelete(objectid){// findif(id==null)thrownewArgumentNullException(nameof(id));TEntityentityToDelete=dbSet.Find(id);// delete findedif(entityToDelete==null)thrownewInvalidOperationException("There is no records with such id");Delete(entityToDelete);}publicvirtualvoidDelete(TEntityentityToDelete){if(entityToDelete==null)thrownewArgumentNullException(nameof(entityToDelete));if(dbContext.Entry(entityToDelete).State==EntityState.Detached){dbSet.Attach(entityToDelete);}dbSet.Remove(entityToDelete);}publicvirtualvoidDelete(Expression<Func<TEntity,bool>>predicate){if(predicate!=null)dbSet.RemoveRange(dbSet.Where(predicate));elsedbSet.RemoveRange(dbSet);}}
Тепер залишилось для кожної сутності реалізувати свій репозиторій. Напишемо інтерфейс, який додаватиме (а можливо і ні) новий функціонал для конкретного репозиторію.
Поле первинного ключа (Identity Field) • Розмітка зовнішніх ключів (Foreign Key Mapping) • Розмітка зв'язків таблиць (Association Table Mapping) • Відображення залежних об'єктів (Dependent Mapping) • Об'єднане значення (Embedded Value) • Серіалізований великий об'єкт (Serialized LOB) • Наслідування з однією таблицею (Single Table Inheritance) • Наслідування з таблицею для кожного класу (Class Table Inheritance) • Наслідування з таблицею для кожного конкретного класу (Concrete Table Inheritance) • Відображення із наслідуванням (Inheritance Mappers) • База даних звітності
Шаблони обробки об'єктно-реляційних метаданих
Відображення на основі метаданих (Metadata Mapping) • Об'єкт-запит (Query Object) • Сховище (Repository)
Збереження стану сеансу на стороні клієнта (Client Session State) • Збереження стану сеансу на стороні сервера (Server Session State) • Збереження стану сеансу в базі даних (Database Session State)