Я предпочитаю этот путь:
if (!typeof arg == 'object') {
//Not JSon
} else {
//Json
}
Прежде всего, как было предложено некоторым ответом, EF сам по себе является шаблоном репозитория, нет необходимости создавать дополнительную абстракцию только для того, чтобы называть его репозиторием.
Mockable Repository для Unit Tests,
Мы позволяем EF общаться для тестирования БД в модульных тестах, чтобы протестировать нашу бизнес-логику прямо против тестовой БД SQL. Я не вижу никакой пользы от макета какого-либо шаблона репозитория. Что действительно не так делает модульные тесты против тестовой базы данных? Поскольку это массовые операции невозможны, и мы заканчиваем тем, что пишем исходный SQL. SQLite в памяти является идеальным кандидатом для проведения модульных тестов с реальной базой данных.
Ненужная абстракция
Вы хотите создать репозиторий так, чтобы в будущем вы могли легко заменить EF на NHbibernate и т. Д. Или что-нибудь еще?
Linq убивает юнит-тесты?
Мне понравятся любые примеры того, как это можно убить.
Injection Dependency, IoC
Ничего себе, это отличные слова, они отлично смотрятся в теории, но иногда вам приходится выбирать компромисс между отличным дизайном и отличным решением. Мы все это использовали, и мы закончили тем, что бросали все на мусор и выбирали другой подход. Размер против скорости (размер кода и скорость разработки) имеет огромное значение в реальной жизни. Пользователям нужна гибкость, им все равно, будет ли ваш код великолепным в дизайне с точки зрения DI или IoC.
Если вы не создаете Visual Studio
Все эти великолепные дизайны необходимы, если вы создаете сложную программу, такую как Visual Studio или Eclipse, которая будет разработана многими людьми, и она должна быть очень настраиваемой. Все большие модели развития вступили в картину после многих лет развития, которые прошли эти IDE, и они эволюционировали на месте, где все эти большие шаблоны дизайна имеют большое значение. Но если вы делаете простую веб-заработную плату или простое деловое приложение, вам лучше развиваться в вашем развитии со временем, а не тратить время на его создание для миллионов пользователей, где он будет развернут только для 100 пользователей.
Репозиторий как фильтрованный вид - ISecureRepository
С другой стороны, репозиторий должен быть фильтрованным представлением EF, который защищает доступ к данным, применяя необходимый наполнитель на основе текущего пользователя / роли.
Но это усложняет репозиторий еще больше, поскольку он заканчивается огромной базой кода для поддержки. Люди создают различные репозитории для разных типов пользователей или комбинации типов сущностей. Не только это, мы также получаем множество DTO.
Следующий ответ представляет собой пример реализации Filtered Repository без создания всего набора классов и методов. Он может не отвечать на вопрос напрямую, но может быть полезным при его получении.
Отказ от ответственности: я являюсь автором Entity REST SDK.
http: //entityrestsdk.codeplex. com
Учитывая вышеизложенное, мы разработали SDK, который создает репозиторий фильтрованного представления на основе SecurityContext, который содержит фильтры для операций CRUD. И только два типа правил упрощают любые сложные операции. Первый - это доступ к сущности, а другой - правило чтения / записи для свойства.
Преимущество заключается в том, что вы не переписываете бизнес-логику или репозитории для разных типов пользователей, просто просто блокируете или предоставляете им доступ ,
public class DefaultSecurityContext : BaseSecurityContext {
public static DefaultSecurityContext Instance = new DefaultSecurityContext();
// UserID for currently logged in User
public static long UserID{
get{
return long.Parse( HttpContext.Current.User.Identity.Name );
}
}
public DefaultSecurityContext(){
}
protected override void OnCreate(){
// User can access his own Account only
var acc = CreateRules<Account>();
acc.SetRead( y => x=> x.AccountID == UserID ) ;
acc.SetWrite( y => x=> x.AccountID == UserID );
// User can only modify AccountName and EmailAddress fields
acc.SetProperties( SecurityRules.ReadWrite,
x => x.AccountName,
x => x.EmailAddress);
// User can read AccountType field
acc.SetProperties<Account>( SecurityRules.Read,
x => x.AccountType);
// User can access his own Orders only
var order = CreateRules<Order>();
order.SetRead( y => x => x.CustomerID == UserID );
// User can modify Order only if OrderStatus is not complete
order.SetWrite( y => x => x.CustomerID == UserID
&& x.OrderStatus != "Complete" );
// User can only modify OrderNotes and OrderStatus
order.SetProperties( SecurityRules.ReadWrite,
x => x.OrderNotes,
x => x.OrderStatus );
// User can not delete orders
order.SetDelete(order.NotSupportedRule);
}
}
Эти правила LINQ оцениваются по методу SaveChanges для базы данных для каждой операции, и эти правила действуют как брандмауэр перед базой данных.
Что наиболее применимо к EF, это не шаблон репозитория. Это шаблон Facade (абстрагирование вызовов EF-методов на более простые и простые в использовании версии).
EF - это тот, который применяет шаблон репозитория (и шаблон Единицы работы). То есть, EF является тем, который абстрагирует уровень доступа к данным, так что пользователь понятия не имеет, что он имеет дело с SQLServer.
И при этом большинство «репозиториев» над EF не являются даже хорошими фасадами, поскольку они просто довольно однозначно, к одиночным методам в EF, даже до того, что они имеют одинаковые сигнатуры.
. Две причины для применения этого так называемого шаблона «Репозиторий» над EF - это упростить тестирования и установить подмножество «консервированных» звонков на него. Неплохо сами по себе, но явно не репозиторий.
Для меня это простое решение с относительно небольшим количеством факторов. Факторы таковы:
Итак, если мое приложение не может оправдать # 2, отдельные модели домена и данных, тогда я обычно не буду беспокоиться о # 5.
ИМО как абстракция Repository
, так и абстракция UnitOfWork
имеют очень ценное место в любом значимом развитии. Люди будут спорить о деталях реализации, но так же, как есть много способов скинуть кошку, есть много способов реализовать абстракцию.
Ваш вопрос конкретно использовать или не использовать и почему.
Как вы, несомненно, поняли, что у вас уже есть оба шаблона, встроенные в Entity Framework, DbContext
- UnitOfWork
, а DbSet
- Repository
. Обычно вам не нужно тестировать единицы UnitOfWork
или Repository
, поскольку они просто облегчают между вашими классами и базовыми реализациями доступа к данным. То, что вам нужно будет делать, снова и снова, издевается над этими двумя абстракциями, когда модуль тестирует логику ваших услуг.
Вы можете высмеивать, подделывать или что угодно с внешними библиотеками, добавляя уровни зависимостей кода (которые вы не контролируете) между логикой, выполняющей тестирование, и проверяемой логикой.
So что ваша собственная абстракция для UnitOfWork
и Repository
дает вам максимальный контроль и гибкость при издевательском тестировании вашего устройства.
Все очень хорошо, но для меня реальная сила этих абстракций это простой способ применения методов Ориентированного программирования и придерживаться принципов SOLID.
Итак, у вас есть IRepository
:
public interface IRepository<T>
where T : class
{
T Add(T entity);
void Delete(T entity);
IQueryable<T> AsQueryable();
}
И его реализация:
public class Repository<T> : IRepository<T>
where T : class
{
private readonly IDbSet<T> _dbSet;
public Repository(PPContext context)
{
_dbSet = context.Set<T>();
}
public T Add(T entity)
{
return _dbSet.Add(entity);
}
public void Delete(T entity)
{
_dbSet.Remove(entity);
}
public IQueryable<T> AsQueryable()
{
return _dbSet.AsQueryable();
}
}
Пока что ничего необычного, но теперь мы хотим добавить некоторые протоколы - легко с помощью журнала Decorator .
public class RepositoryLoggerDecorator<T> : IRepository<T>
where T : class
{
Logger logger = LogManager.GetCurrentClassLogger();
private readonly IRepository<T> _decorated;
public RepositoryLoggerDecorator(IRepository<T> decorated)
{
_decorated = decorated;
}
public T Add(T entity)
{
logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() );
T added = _decorated.Add(entity);
logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
return added;
}
public void Delete(T entity)
{
logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
_decorated.Delete(entity);
logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
}
public IQueryable<T> AsQueryable()
{
return _decorated.AsQueryable();
}
}
Все сделанные и без изменения нашего существующего кода. Есть много других проблем, связанных с перекрестными циклами, которые мы можем добавить: обработка исключений, кэширование данных, валидация данных или что-то еще, а также на протяжении всего нашего процесса проектирования и сборки наиболее ценная вещь, которая у нас есть, что позволяет нам добавлять простые функции, не меняя какой-либо существующий код является нашей абстракцией IRepository
.
Теперь, много раз я видел этот вопрос в StackOverflow - «как вы заставляете Entity Framework работать в среде с несколькими арендаторами?».
https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant
Если у вас есть абстракция Repository
, тогда ответ «легко» добавьте декоратор "
public class RepositoryTennantFilterDecorator<T> : IRepository<T>
where T : class
{
//public for Unit Test example
public readonly IRepository<T> _decorated;
public RepositoryTennantFilterDecorator(IRepository<T> decorated)
{
_decorated = decorated;
}
public T Add(T entity)
{
return _decorated.Add(entity);
}
public void Delete(T entity)
{
_decorated.Delete(entity);
}
public IQueryable<T> AsQueryable()
{
return _decorated.AsQueryable().Where(o => true);
}
}
IMO, вы всегда должны размещать простую абстракцию над любым сторонним компонентом, на который будут ссылаться более чем в нескольких местах. С этой точки зрения ORM является идеальным кандидатом, поскольку он упоминается во многих наших кодексах.
Ответ, который обычно приходит на ум, когда кто-то говорит «почему я должен иметь абстракцию (например, Repository
), над той или иной сторонней библиотекой «есть», почему бы и нет? »
PS Декораторы чрезвычайно просты в применении с использованием контейнера IoC, такого как SimpleInjector .
[TestFixture]
public class IRepositoryTesting
{
[Test]
public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository()
{
Container container = new Container();
container.RegisterLifetimeScope<PPContext>();
container.RegisterOpenGeneric(
typeof(IRepository<>),
typeof(Repository<>));
container.RegisterDecorator(
typeof(IRepository<>),
typeof(RepositoryLoggerDecorator<>));
container.RegisterDecorator(
typeof(IRepository<>),
typeof(RepositoryTennantFilterDecorator<>));
container.Verify();
using (container.BeginLifetimeScope())
{
var result = container.GetInstance<IRepository<Image>>();
Assert.That(
result,
Is.InstanceOf(typeof(RepositoryTennantFilterDecorator<Image>)));
Assert.That(
(result as RepositoryTennantFilterDecorator<Image>)._decorated,
Is.InstanceOf(typeof(RepositoryLoggerDecorator<Image>)));
}
}
}
Существует много споров о том, какой метод является правильным, поэтому я рассматриваю его как приемлемый, поэтому я использую все, что мне больше всего нравится (в котором нет репозитория, UoW).
В EF UoW реализуется через DbContext, а DbSets - это хранилища.
Что касается того, как работать с уровнем данных, я просто непосредственно работаю над объектом DbContext, для сложных запросов я буду применять методы расширения для запроса, который можно использовать повторно.
Я считаю, что у Ayende также есть некоторые сообщения о том, как абстрагироваться от CUD-операций.
Я всегда создаю интерфейс и наследую свой контекст, поэтому я могу использовать контейнер IoC для Д.И..
Репозиторий (или, тем не менее, один из них хочет называть его) в это время для меня, в основном касается абстрагирования слоя сохранения.
Я использую его в сочетании с объектами запроса, поэтому у меня нет связи с какой-либо конкретной технологией в моих приложениях. Кроме того, это значительно облегчает тестирование.
Итак, я имею тенденцию иметь
public interface IRepository : IDisposable
{
void Save<TEntity>(TEntity entity);
void SaveList<TEntity>(IEnumerable<TEntity> entities);
void Delete<TEntity>(TEntity entity);
void DeleteList<TEntity>(IEnumerable<TEntity> entities);
IList<TEntity> GetAll<TEntity>() where TEntity : class;
int GetCount<TEntity>() where TEntity : class;
void StartConversation();
void EndConversation();
//if query objects can be self sustaining (i.e. not need additional configuration - think session), there is no need to include this method in the repository.
TResult ExecuteQuery<TResult>(IQueryObject<TResult> query);
}
Возможно, добавить асинхронные методы с обратными вызовами в качестве делегатов. Репо легко реализовать в целом, поэтому я не могу коснуться строки реализации из приложения в приложение. Ну, это правда, по крайней мере, при использовании NH, я сделал это также с EF, но сделал меня ненавидеть EF. 4. Разговор - это начало транзакции. Очень классно, если несколько классов совместно используют экземпляр репозитория. Кроме того, для NH одно репо в моей реализации равно одному сеансу, который открывается при первом запросе.
Затем объекты запроса
public interface IQueryObject<TResult>
{
/// <summary>Provides configuration options.</summary>
/// <remarks>
/// If the query object is used through a repository this method might or might not be called depending on the particular implementation of a repository.
/// If not used through a repository, it can be useful as a configuration option.
/// </remarks>
void Configure(object parameter);
/// <summary>Implementation of the query.</summary>
TResult GetResult();
}
Для конфигурации, которую я использую только в NH пройти в ISession. В EF нет смысла более или менее.
Примерный запрос будет ... (NH)
public class GetAll<TEntity> : AbstractQueryObject<IList<TEntity>>
where TEntity : class
{
public override IList<TEntity> GetResult()
{
return this.Session.CreateCriteria<TEntity>().List<TEntity>();
}
}
Чтобы выполнить запрос EF, вам нужно будет иметь контекст в базе данных Abstract, а не в сеансе. Но, конечно, ifc будет одинаковым.
Таким образом, сами запросы инкапсулируются и легко проверяются. Лучше всего, мой код опирается только на интерфейсы. Все очень чисто. Доменные (бизнес-объекты) - это именно то, что, например, не происходит смешивания обязанностей, например, при использовании активного шаблона записи, который вряд ли тестируется, и смешивает код доступа к данным (запрос) в объекте домена и при этом смешивает проблемы (объект, который извлекает себя?). Все могут свободно создавать POCO для передачи данных.
В целом, многократное повторное использование кода и простота обеспечивается этим подходом при потере того, что я могу себе представить. Любые идеи?
И большое спасибо Айенде за его большие посты и постоянную преданность делу. Его идеи здесь (объект запроса), а не мои.
Шаблон репозитория представляет собой абстракцию . Цель состоит в том, чтобы уменьшить сложность и сделать остальной код неустойчивым. В качестве бонуса вы можете написать тесты unit вместо тестов integration .
Проблема в том, что многие разработчики не понимают цель шаблонов и создают репозитории, которые вызывают утечку конкретной информации до вызывающего абонента (как правило, выставляя IQueryable<T>
). Поступая таким образом, они не получают никакой пользы от использования 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 . Это дает вам полную абстракцию, которая также может быть проверкой.)
Linq - это теперь «репозиторий».
ISession + Linq уже является репозиторием, и вам не нужны ни методы GetXByY
, ни QueryData(Query q)
. Будучи немного параноидальным для использования DAL, я по-прежнему предпочитаю интерфейс репозитория. (С точки зрения ремонтопригодности мы также должны иметь некоторый фасад над конкретными интерфейсами доступа к данным).
Здесь используется репозиторий - он отключает нас от прямого использования nhibernate, но предоставляет интерфейс linq (как доступ ISession в исключительных случаях, которые в конечном итоге подлежат рефактору).
class Repo
{
ISession _session; //via ioc
IQueryable<T> Query()
{
return _session.Query<T>();
}
}
Я пошел по многим путям и создал множество реализаций репозиториев в разных проектах и ... Я бросил полотенце и отказался от него, вот почему.
Кодирование исключения
Вы кодируете для 1% вероятность того, что ваша база данных изменится с одной технологии на другую? Если вы думаете о будущем состоянии своего бизнеса и говорите «да», то это возможность: а) у них должно быть много денег, чтобы позволить себе переход на другую технологию БД или б) вы выбираете технологию БД для развлечения или с ) что-то ушло ужасно неправильно с первой технологией, которую вы решили использовать.
Зачем выбрасывать богатый синтаксис LINQ?
LINQ и EF были разработаны так, чтобы вы могли делать с ним аккуратные вещи читать и пересекать графы объектов. Создание и поддержка репозитория, который может дать вам такую же гибкость, чтобы сделать это, является чудовищной задачей. По моему опыту в любое время, когда я создал репозиторий, я ВСЕГДА имел деловую логику, протекающую в слое репозитория, чтобы сделать запросы более эффективными и / или уменьшить количество обращений к базе данных.
Я не хочу создавать метод для каждой перестановки запроса, который я должен написать. Я мог бы также написать хранимые процедуры. Я не хочу GetOrder, GetOrderWithOrderItem, GetOrderWithOrderItemWithOrderActivity, GetOrderByUserId и т. Д. Я просто хочу получить основную сущность и пройти и включить граф объектов так, как вам нравится.
Большинство примеров репозиториев это фигня
Если вы не разрабатываете что-то ДЕЙСТВИТЕЛЬНО голые кости, как блог или что-то, что ваши запросы никогда не будут такими же простыми, как 90% примеров, которые вы найдете в Интернете, окружающих шаблон репозитория. Я не могу подчеркнуть это достаточно! Это то, что нужно проползать через грязь, чтобы понять. Всегда будет один запрос, который сломает ваш прекрасно продуманный репозиторий / решение, которое вы создали, и его не до тех пор, пока не начнется то, на что вы догадаетесь, и технический долг / эрозия.
t unit test me bro
Но как насчет модульного тестирования, если у меня нет репозитория? Как я буду издеваться? Просто вы этого не делаете. Давайте посмотрим на это с обоих углов:
Нет репозитория - вы можете издеваться над DbContext с помощью IDbContext или некоторых других трюков, но тогда вы действительно тестируете LINQ для объектов, а не LINQ to Entities, потому что запрос определяется во время выполнения ... ОК, так что это не хорошо! Итак, теперь это до теста интеграции, чтобы покрыть это.
С репозиторием - теперь вы можете издеваться над своими репозиториями и модулями тестировать слой (ы) между ними. Отлично? Ну, не так ... В вышеприведенных случаях, когда вам нужно протекать логику в уровень репозитория, чтобы сделать запросы более эффективными и / или менее попадающими в базу данных, как ваши обходные тесты могут покрыть это? Теперь он находится на уровне репо, и вы не хотите правильно тестировать IQueryable? Также давайте честно сказать, что ваши юнит-тесты не будут охватывать запросы, у которых есть 20 строк .Where()
, а .Include()
- связка связей и снова попадает в базу данных, чтобы делать все это другое: blah, blah, blah в любом случае, потому что запрос создается во время выполнения. Кроме того, поскольку вы создали хранилище, чтобы сохранить приоритет на верхних уровнях невежественным, если теперь вы хотите изменить технологию баз данных, извините, что ваши юнит-тесты вызывающе не гарантируют одинаковые результаты во время выполнения, вернутся к интеграционным тестам. Таким образом, вся точка репозитория кажется странной ..
2 цента
Мы уже теряем много функциональности и синтаксиса при использовании EF поверх простых хранимых процедур (объемные вставки, массовые удаления, CTE и т. Д.), Но я также код на C #, поэтому мне не нужно вводить двоичный код. Мы используем EF, поэтому у нас есть возможность использовать разные провайдеры и работать с объектными графами в хорошо связанном ключе среди многих вещей. Некоторые абстракции полезны, а некоторые нет.
Надеюсь, это поможет кому-то в интернетах где-то ...
Coding for the exception
: Использование репозиториев не позволяет переключать механизм базы данных. Речь идет о том, чтобы отделить бизнес от настойчивости.
– jgauffin
10 July 2014 в 08:05
DbSet
является хранилищем i>, а DbContext
является единицей работы . Зачем использовать шаблон репозитория, когда ORM уже делает это для нас! Для тестирования просто измените поставщика на InMemory
. И делайте свои тесты! Он хорошо документирован в MSDN.
– Mohammed Noureldin
22 November 2017 в 13:01