Шаблон репозитория представляет собой абстракцию . Цель состоит в том, чтобы уменьшить сложность и сделать остальной код неустойчивым. В качестве бонуса вы можете написать тесты unit вместо тестов integration .
Проблема в том, что многие разработчики не понимают цель шаблонов и создают репозитории, которые вызывают утечку конкретной информации до вызывающего абонента (как правило, выставляя IQueryable
). Поступая таким образом, они не получают никакой пользы от использования OR / M напрямую.
Кодирование исключения
Использование репозиториев не о возможность переключения технологии сохранения (т. е. изменение базы данных или использование webservice и т. д.). Речь идет об отделении бизнес-логики от настойчивости, чтобы уменьшить сложность и взаимодействие.
Тестирование модулей против тестов интеграции
Вы не пишете модульные тесты для репозиториев. период.
Но, вводя репозитории (или любой другой уровень абстракции между устойчивостью и бизнесом), вы можете писать модульные тесты для бизнес-логики. т. е. вам не нужно беспокоиться о том, что ваши тесты не выполняются из-за неправильно настроенной базы данных.
Что касается запросов. Если вы используете LINQ, вам также необходимо убедиться, что ваши запросы работают так же, как и с репозиториями. и это делается с помощью тестов интеграции.
Разница в том, что если вы не смешивали свою бизнес с операторами LINQ, вы можете быть на 100% уверены, что это ваш код настойчивости, который терпит неудачу, а не что-то еще.
Если вы проанализируете свои тесты, вы также увидите, что они намного чище, если у вас нет смешанных проблем (например, LINQ + Business logic)
Примеры репозитория
Большинство примеров это дерьмо. Это очень верно. Однако, если у вас есть какой-либо шаблон дизайна, вы найдете множество дерьмовых примеров. Это не повод избежать использования шаблона.
Создание правильной реализации репозитория очень просто. Фактически, вы должны следовать только одному правилу:
Не добавляйте ничего в класс репозитория до самого момента, когда вам это нужно
A много кодеров ленивы и пытается создать общий репозиторий и использовать базовый класс с множеством методов, которые они могли бы нуждаться. YAGNI. Вы пишете класс хранилища один раз и сохраняете его до тех пор, пока приложение живет (может быть лет). Зачем трахать его, ленив. Держите его в чистоте без наследования базового класса. Это облегчит чтение и поддержку.
(Вышеуказанное утверждение является ориентиром, а не законом. Базовый класс может быть очень мотивирован. Подумайте, прежде чем добавлять его, чтобы добавить его по правильным причинам)
Вывод:
Если вы не возражаете, если в своем бизнес-коде не указаны заявления LINQ и не заботитесь об модульных тестах, я не вижу причин для не использовать Entity Framework напрямую.
Обновить
Я написал как о шаблоне репозитория, так и о том, что на самом деле означает «абстракция»: http://blog.gauffin.org / 2013/01 / repository-pattern-done-right /
Обновление 2
Для одного типа сущности с 20 + полями, как вы будете создавать запрос метод поддержки любой комбинации перестановок? Вы не хотите ограничивать поиск только по имени, как насчет поиска с навигационными свойствами, перечислить все заказы с элементом с конкретным ценовым кодом, 3 уровня поиска навигационной собственности. Вся причина
blockquote>IQueryable
была изобретена в том, чтобы иметь возможность составлять любую комбинацию поиска по базе данных. Все выглядит великолепно в теории, но потребность пользователя выигрывает выше теории.Опять же: объект с 20 + полями неправильно смоделирован. Это объект БОГА. Сломай.
Я не утверждаю, что
IQueryable
не был сделан для запроса. Я говорю, что это неправильно для слоя абстракции, такого как шаблон репозитория , поскольку он протекает. Нет ни одного 100% -ного провайдера LINQ To Sql (например, EF).У всех есть конкретные вещи, связанные с реализацией, например, как использовать нетерпеливую / ленивую загрузку или как выполнять SQL-запросы «IN». Выставление
IQueryable
в репозитории заставляет пользователя знать все эти вещи. Таким образом, вся попытка абстрагировать источник данных - это полный сбой. Вы просто добавляете сложность, не получая никакой пользы от использования OR / M напрямую.Либо правильно создавайте шаблон хранилища, либо просто не используйте его вообще.
(if вы действительно хотите обрабатывать большие объекты, вы можете комбинировать шаблон репозитория с шаблоном Specification . Это дает вам полную абстракцию, которая также может быть проверкой.)
Мы можем использовать %in%
- filter
строки после группировки по 'sentId` и' partner. '
library(dplyr)
df1 %>%
group_by(sentId., partner.) %>%
filter(3 %in% label.)
# A tibble: 3 x 5
# Groups: sentId. [2]
# sentId. B. label. partner. code
# <dbl> <dbl> <dbl> <dbl> <int>
#1 1 2 3 4 123
#2 1 2 2 4 124
#3 4 2 3 8 125
Или в компактном виде с data.table
[1110 ]
library(data.table)
setDT(df1)[, .SD[3 %in% label.], .(sentId., partner.)]
<час> Или с base R
df1[with(df1, ave(label.==3, sentId., partner., FUN = any)),]
df1 <- structure(list(sentId. = c(1, 1, 4, 7), B. = c(2, 2, 2, 3), label. = c(3,
2, 3, 2), partner. = c(4, 4, 8, 7), code = 123:126),
class = "data.frame", row.names = c(NA,
-4L))
Использование sqldf
: извлекает sentID
и partner
с меткой 3 как два внутренних запроса и извлекает результат из него.
names(df) <- gsub("\\.", "", names(df)) # to remove . from column name
sqldf("select * from df where (sentID IN (select sentID from df where label IS 3) OR
partner IN (select partner from df where label IS 3))")
Выход:
sentId B label partner code
1 1 2 3 4 123
2 1 2 2 4 124
3 4 2 3 8 125
Эта проблема может быть легко сформулирована с использованием SQL, поэтому одним из вариантов будет использование библиотеки sqldf
:
library(sqldf)
# your data frame df
sql <- "SELECT t1.\"sentId.\", t1.\"B.\", t1.\"label.\", t1.\"partner.\", t1.code
FROM yourTable t1
WHERE t1.\"label.\" = '3.' OR
EXISTS (SELECT 1 FROM yourTable t2
WHERE t1.\"sentId.\" = t2.\"sentId.\" AND
t1.\"partner.\" = t2.\"partner.\" AND
t2.\"label.\" = '3.')"
result <- sqldf(sql)
[ 118]
Примечание. В приведенной выше демонстрации фактически используется MariaDB, поскольку SQLite не работал с демонстрационным инструментом. Но это все еще показывает, что логика запроса верна.
Сначала мы можем найти индексы строк, в которых у нас есть интересующее значение label
, а затем использовать эти индексы для подмножества значений sentId
и partner
из всего кадра данных.
label_value <- 3
inds <- df$label == label_value
df[with(df, sentId %in% sentId[inds] & partner %in% partner[inds]), ]
# sentId B label partner code
#1 1 2 3 4 123
#2 1 2 2 4 124
#3 4 2 3 8 125
Та же логика в dplyr
будет
library(dplyr)
df %>%
filter(sentId %in% sentId[label == label_value] &
partner %in% partner[label == label_value])