Repository

disclaimer: the article provides an overview of the specific pattern Repository implementation,considering in detail the methods and features within Incoding Framework. For the best immersion read Repository by Fowler, CQRS vs NLayer

UPD: Article source codes are available on GitHub

What do we get ?

Patterns often become not only a tool to struggle against the complexity of the project, but sometimes at a very “dense” use, are themselves a source of problems. So before applying one or another pattern I have been studying its impact on the architecture of the project and the amount of code to implement it.

  • Abstraction over the database

Note: ORM is an abstraction over the database and Repository will be redundant, but eventually I came to the conclusion that it is always better to have a layer between the third-party components, such as ORM, IoC and others.

  • Uniform interface for providers (Nhibernate, Entity Framework, etc.)

Note: The first part partly covers the second point, but it is a different problem. 

And may it be without it?

DataContext on View (linq to sql used the name DataContext for a class of database access), a kind of “meme” in the development of web sites, which is on par with GOD object and other anti-patterns. In fact, a call does not necessarily have to be on View, because when calling DataContext from Controller or Service, the problem remains. What difficulties the use of Data Context without Repository entails:

    • Binding to a specific ORM implementation ( Linq to sql = DataContext, Nhibernate = ISession )

 

Note: on the one hand ORM replacement in the later stages of the project seems to be a controversial and a difficult step, but personal experience convinced (Linq to Sql replacement for Nhibernate), it is sometimes the only way to solve some problems.

  • There are a lot of low-level methods

Note: I hold the opinion that it is much easier to maintain a limited set of methods to work with the database than access to low-level features of a particular ORM operation. 

  • It is difficult to monitor places where the work with a database is going on (Incoding Framework provides access to the Repository only in Query and Command)

Note: The main difficulty will be to support UnitOfWork to ensure correct operation of transactions

  • The difficulties of writing unit tests

Note: due to the fact that the code works with 3rd-party objects, then there is difficulty in creating Mock Up

 

CRUD

Repository has methods to perform basic tasks related to the creation (create), reading (read), updating (update) and removing (delete):

Create, Update, Delete

  • Save – – saves the object to the database

  •  Delete – deletes the object according to the type and Id

  • SaveOrUpdate –  – saves or updates the object in the database

Note: SaveOrUpdate method may not be used, because many ORM (Nhibernate, Entity Framework) supports object state tracking (tracking), but if the provider does not have this opportunity, you should always call SaveOrUpdate

Read

  • GetById -returns an object according to the type and Id

Note: Id parameter is Object, the reason for the absence of a specific type is to maximize flexibility (Id can be string, int, long, guid) of the solution.
Note: If Id null or an object is found, the return will be null

  • LoadById – – returns an object according to the type and Id

Note: LoadById method works just as well as GetById, with the difference that when calling Load will try to find objects in the Cache, and Get always calls the database

  •  Query – returns a set of (IQueryable) objects based on the specifications (where, order, fetch, paginated)

Note: The operations manual of the where, order, fetch query specifications are discussed below.

  • Paginated – returns paginated result on the basis of specifications (where, order, fetch, paginated)

Note: IncPaginatedResult is an object that was designed to provide a comfortable work with the results that are to be displayed per page. Practice has shown that to construct paged data, you need to know the general (TotalCounts) number of elements, excluding pages and items (Items).

  • Total Counts  – the total number of items in the database ( include where )
  • Items – items that are in the range of the current page

Note: Paginated Specification algorithm will be discussed below

Specification

In C # there is LINQ, which allows to build a query plan, and to implement (broadcast, transmit) them by way of a certain provider. For example, if we chose Nhibernate, as ORM for our application, IQueryable Nhibernate provider can be used in order to transmit conditions (where, order, fetch, paginated) in SQL.

What is bad about LINQ?
Specifications are cover of LINQ expressions into separate classes, which offer the following benefits:

  • Reuse in different Query

Note: a significant advantage, because when writing large projects, it is difficult to maintain “scattered” throughout the code LINQ expressions.

  • Substitution for mock-up objects in Query tests

Note: tests of the specifications will be carried out separately

  • Encapsulation of additional logic

Sample



 примечание: дополнительная логика, также актуальная и для Fetch и Order спецификаций

А может есть решения ?

Если не использовать specification, то можно применять C# extensions

примечание: данное решение обладает минусом, потому  что не получится протестировать Query без выполнения кода Extesnions.

Where

Implementation

All specifications for filtration are inherited from the base class Specification (TEntity is the type of the Query object), which has an abstract method IsSatisfiedBy.

Note: instead of null if code value is empty, expression product => true can be returned, but then in the resulting SQL query conditions will be deprived that would not have logical load.

Use

Note: specification can be put together by way of And, Or and Not

Linq

Order

Реализация

Все спецификации для сортировки наследуются от базового класса OrderSpecification<TEntity> ( TEntity – это тип объекта Query ), который имеет абстрактный метод SortedBy.

  • Order(r= >r.Name,OrderType.Ascending)  – сортировка Name по возрастанию

примечание: OrderBy(r= >r.Name) сокращенный вариант

  • Order(r= >r.Name,OrderType.Descending)  – сортировка Name по убыванию

примечание: OrderByDescending(r= >r.Name) сокращенный вариант 

Использование

Linq аналог

Paginated

Реализация

PaginatedSpecification это уже готовый класс, так что наследников делать не надо, а создаем новый экземпляр через  конструктор ( CurrentPage и PageSize )

  •  Current Page – указывает какую страницу выбирать
  •  Page Size – указывает сколько элементов  на странице

Использование

примечание: если в  базе данных содержится 50 записей, то вернутся записи с 10 по 20 

Linq аналог

Fetch

Реализация

Все спецификации для выборки наследуются от базового класса FetchSpecification<TEntity> ( TEntity – это тип объекта Query ), который имеет абстрактный метод FetchedBy.

  •  Join   – подгружает  элемент по связи One to Many

примечание: можно получать доступ к полям выбранного элемента 

  • JoinMany – подгружает  элементы по связи Many to  One / Many to Many

примечание: можно получить доступ к полям каждого выбранного элемента

Linq аналог

Каждый ORM имеет свой способ выборки элементов

Актуальность

  • Fetch нужен когда сценарий Query возвращает объекты базы данных, а не подготовленную “плоскую” модель, но после прихода MVD, данный сценарий практический не востребован.
  • Основное применение Fetch это возможность ускорить Query за счет выборки всех данных за один запрос, но как показала практика, лучшим решением будет построение OLAP системы ( пример реализации можно посмотреть в Browsio )

примечание: тема OLAP  сложная и хорошо освещена в интернете, но в ближайшие время появится статья с обзором решений в рамках Incoding Framework

Features

To applicate Repository effectively in Incoding Framework, it is necessary to consider the following points:

  • Repository works in the context of the Unit of Work and therefore the transaction will not be closed until all the code Command is successfully implemented.

  • Query works with Repository in ReadUncommited mode

Note: it says that saving or changes cannot be made in the database

  • Connection to the database is available only in the context of Command and Query

Пример



Опасность представляет  gap.Type.Name, потому что  мы обращаемся к дочерней таблице, что заставляет ORM ( если не выключено LazyLoad или добавлен  соответствующий для поля Fetch ) делать запрос в базу данных, для того, чтобы подгрузить элемент, поэтому если мы вернем List<Gap> на Controller и там обратимся к поле gap.Type.Name, то получим exception.

Vlad Kopachinsky

I am a Senior developer at Incoding Software in Russian Federation. I created the Incoding Framework for rapid development

More Posts - Website - Twitter - Facebook - LinkedIn

Leave a Reply