Ця стаття має кілька недоліків. Будь ласка, допоможіть удосконалити її або обговоріть ці проблеми на .
|
Repository — патерн, який розділяє рівні джерела даних і логіки програми. Часто використовується із патерном Unit Of Work
Переваги та недоліки
Цей розділ має вигляд переліку, який краще подати . (15 червня 2019) |
Переваги
- Використовується, як колекція
- Інкапсулює великі запити до БД в методи
- Рівень абстракції між Business Logic рівнем та Data Access рівнем
- Ізолює програму від змін джерела даних
- Джерело даних може бути змінено без будь-яких змін в бізнес логіці і з мінімальними змінами в Репозиторії
- Полегшує автоматизоване юніт тестування, Test Driven Development
- Легко створювати mock репозиторію
Недоліки
- Зростає кількість класів
- Погіршує продуктивність
- Обмежує у використанні особливостей ОРМ фреймворку
Опис мовою C#
Використаємо Entity Framework. Нехай дано клас-сутність User
public class User { public int Id { get; set; } public string Name { get; set; } }
Тепер напишемо інтерфейс репозиторію. Варто зазначити, що репозиторій є колекцією, і має поводитись як колекція, та не містити методів Update(), Save() тощо. Також однією із великих помилок, є те що методи повертають IQueryable замість IEnumerable. Якщо повертати IQueryable це дозволить надбудувати над запитом, ще запити, що не є вірним, оскільки мета цього патерну якраз і є уникнення великих запитів. В такому разі, краще написати ще один метод, який буде виконувати більший запит.
public interface IRepository<TEntity> where TEntity : class { int Count(); int Count(Expression<Func<TEntity, bool>> predicate); IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "", int? page = null, int? amount = null); TEntity Get(int id); void Insert(TEntity entity); void Delete(object id); void Delete(TEntity entityToDelete); void Delete(Expression<Func<TEntity, bool>> predicate); }
Тепер реалізуємо цей інтерфейс у вигляді узагальненого класу. При реалізації ми повертаємо сутність, а не DTO. Мапування — це не відповідальність репозиторію.
public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class { // FIELDS // узагальнений контекст protected DbContext dbContext; protected DbSet<TEntity> dbSet; // CONSTRUCTORS public GenericRepository(DbContext dbContext) { this.dbContext = dbContext; this.dbSet = dbContext.Set<TEntity>(); } // METHODS public virtual int Count() { return dbSet.Count(); } public virtual int Count(Expression<Func<TEntity, bool>> predicate) { return dbSet.Count(predicate); } public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "", int? page = null, int? amount = null) { // filter IQueryable<TEntity> query = dbSet; if (filter != null) { query = query.Where(filter); } // include properties foreach (string includeProperty in includeProperties.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } // ordering if (orderBy != null) query = orderBy(query); // paging if (page.HasValue && amount.HasValue) query = query.Skip((page.Value - 1) * amount.Value).Take(amount.Value); return query; } public virtual TEntity Get(int id) { return dbSet.Find(id); } public virtual void Insert(TEntity entity) { dbSet.Add(entity); } public virtual void Delete(object id) { // find if (id == null) throw new ArgumentNullException(nameof(id)); TEntity entityToDelete = dbSet.Find(id); // delete finded if (entityToDelete == null) throw new InvalidOperationException("There is no records with such id"); Delete(entityToDelete); } public virtual void Delete(TEntity entityToDelete) { if (entityToDelete == null) throw new ArgumentNullException(nameof(entityToDelete)); if (dbContext.Entry(entityToDelete).State == EntityState.Detached) { dbSet.Attach(entityToDelete); } dbSet.Remove(entityToDelete); } public virtual void Delete(Expression<Func<TEntity, bool>> predicate) { if (predicate != null) dbSet.RemoveRange(dbSet.Where(predicate)); else dbSet.RemoveRange(dbSet); } }
Тепер залишилось для кожної сутності реалізувати свій репозиторій. Напишемо інтерфейс, який додаватиме (а можливо і ні) новий функціонал для конкретного репозиторію.
public interface IUserRepository: IRepository<User> { User GetByName(string name); }
Та конкретна реалізація:
public class UserRepository : GenericRepository<User>, IUserRepository { public UserRepository(DbContext dbContext) : base(dbContext) { } public User GetByName(string name) { return dbSet.First(u => u.Name == name); } }
При потребі варто також узагальнювати тип ключа:
public interface IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey> { . . . }
Зв'язок з іншими патернами
- Unit Of Work та Repository часто використовують в парі.
Реалізація
Цей розділ статті ще . (15 червня 2019) |
Див. також
Джерела
- Implementing the repository and unit of work patterns [ 14 вересня 2020 у Wayback Machine.]
- Repository and unit of work pattern [ 10 серпня 2020 у Wayback Machine.]
- Common mistakes with the repository pattern [ 13 червня 2019 у Wayback Machine.]
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Cya stattya maye kilka nedolikiv Bud laska dopomozhit udoskonaliti yiyi abo obgovorit ci problemi na Cya stattya ne maye interviki posilan Vi mozhete dopomogti proyektu znajshovshi ta dodavshi yih do vidpovidnogo elementu Vikidanih 15 chervnya 2019 Cya stattya mistit perelik posilan ale pohodzhennya tverdzhen u nij zalishayetsya nezrozumilim cherez praktichno povnu vidsutnist vnutrishnotekstovih dzherel vinosok Bud laska dopomozhit polipshiti cyu stattyu peretvorivshi dzherela z pereliku posilan na dzherela vinoski u samomu teksti statti 15 chervnya 2019 Vstupnij rozdil ciyeyi statti jmovirno nespovna pidsumovuye klyuchovi tezi yiyi vmistu Bud laska dopomozhit rozshiriti vstup dodavshi stislij oglyad najvazhlivishih aspektiv statti 15 chervnya 2019 Repository patern yakij rozdilyaye rivni dzherela danih i logiki programi Chasto vikoristovuyetsya iz paternom Unit Of WorkUML diagrama sho opisuye strukturu shablonu proyektuvannya RepositoryPerevagi ta nedolikiCej rozdil maye viglyad pereliku yakij krashe podati prozoyu Vi mozhete dopomogti viklasti spisok prozoyu de ce dorechno Oznajomtesya z dovidkoyu z redaguvannya 15 chervnya 2019 Perevagi Vikoristovuyetsya yak kolekciya Inkapsulyuye veliki zapiti do BD v metodi Riven abstrakciyi mizh Business Logic rivnem ta Data Access rivnem Izolyuye programu vid zmin dzherela danih Dzherelo danih mozhe buti zmineno bez bud yakih zmin v biznes logici i z minimalnimi zminami v Repozitoriyi Polegshuye avtomatizovane yunit testuvannya Test Driven Development Legko stvoryuvati mock repozitoriyu Nedoliki Zrostaye kilkist klasiv Pogirshuye produktivnist Obmezhuye u vikoristanni osoblivostej ORM frejmvorkuOpis movoyu C Vikoristayemo Entity Framework Nehaj dano klas sutnist User public class User public int Id get set public string Name get set Teper napishemo interfejs repozitoriyu Varto zaznachiti sho repozitorij ye kolekciyeyu i maye povoditis yak kolekciya ta ne mistiti metodiv Update Save tosho Takozh odniyeyu iz velikih pomilok ye te sho metodi povertayut IQueryable zamist IEnumerable Yaksho povertati IQueryable ce dozvolit nadbuduvati nad zapitom she zapiti sho ne ye virnim oskilki meta cogo paternu yakraz i ye uniknennya velikih zapitiv V takomu razi krashe napisati she odin metod yakij bude vikonuvati bilshij zapit public interface IRepository lt TEntity gt where TEntity class int Count int Count Expression lt Func lt TEntity bool gt gt predicate IEnumerable lt TEntity gt Get Expression lt Func lt TEntity bool gt gt filter null Func lt IQueryable lt TEntity gt IOrderedQueryable lt TEntity gt gt orderBy null string includeProperties int page null int amount null TEntity Get int id void Insert TEntity entity void Delete object id void Delete TEntity entityToDelete void Delete Expression lt Func lt TEntity bool gt gt predicate Teper realizuyemo cej interfejs u viglyadi uzagalnenogo klasu Pri realizaciyi mi povertayemo sutnist a ne DTO Mapuvannya ce ne vidpovidalnist repozitoriyu public class GenericRepository lt TEntity gt IRepository lt TEntity gt where TEntity class FIELDS uzagalnenij kontekst protected DbContext dbContext protected DbSet lt TEntity gt dbSet CONSTRUCTORS public GenericRepository DbContext dbContext this dbContext dbContext this dbSet dbContext Set lt TEntity gt METHODS public virtual int Count return dbSet Count public virtual int Count Expression lt Func lt TEntity bool gt gt predicate return dbSet Count predicate public virtual IEnumerable lt TEntity gt Get Expression lt Func lt TEntity bool gt gt filter null Func lt IQueryable lt TEntity gt IOrderedQueryable lt TEntity gt gt orderBy null string includeProperties int page null int amount null filter IQueryable lt TEntity gt query dbSet if filter null query query Where filter include properties foreach string includeProperty in includeProperties Split new char StringSplitOptions RemoveEmptyEntries query query Include includeProperty ordering if orderBy null query orderBy query paging if page HasValue amp amp amount HasValue query query Skip page Value 1 amount Value Take amount Value return query public virtual TEntity Get int id return dbSet Find id public virtual void Insert TEntity entity dbSet Add entity public virtual void Delete object id find if id null throw new ArgumentNullException nameof id TEntity entityToDelete dbSet Find id delete finded if entityToDelete null throw new InvalidOperationException There is no records with such id Delete entityToDelete public virtual void Delete TEntity entityToDelete if entityToDelete null throw new ArgumentNullException nameof entityToDelete if dbContext Entry entityToDelete State EntityState Detached dbSet Attach entityToDelete dbSet Remove entityToDelete public virtual void Delete Expression lt Func lt TEntity bool gt gt predicate if predicate null dbSet RemoveRange dbSet Where predicate else dbSet RemoveRange dbSet Teper zalishilos dlya kozhnoyi sutnosti realizuvati svij repozitorij Napishemo interfejs yakij dodavatime a mozhlivo i ni novij funkcional dlya konkretnogo repozitoriyu public interface IUserRepository IRepository lt User gt User GetByName string name Ta konkretna realizaciya public class UserRepository GenericRepository lt User gt IUserRepository public UserRepository DbContext dbContext base dbContext public User GetByName string name return dbSet First u gt u Name name Pri potrebi varto takozh uzagalnyuvati tip klyucha public interface IRepository lt TEntity TKey gt where TEntity class IEntity lt TKey gt Zv yazok z inshimi paternamiUnit Of Work ta Repository chasto vikoristovuyut v pari RealizaciyaCej rozdil statti she ne napisano Vi mozhete dopomogti proyektu napisavshi jogo 15 chervnya 2019 Div takozhShabloni proyektuvannya programnogo zabezpechennya Ob yektno oriyentovane programuvannyaDzherelaImplementing the repository and unit of work patterns 14 veresnya 2020 u Wayback Machine Repository and unit of work pattern 10 serpnya 2020 u Wayback Machine Common mistakes with the repository pattern 13 chervnya 2019 u Wayback Machine