Должен ли я избавиться от своих репозиториев и работать с помощью EF Core dbcontext [duplicate]

Я предпочитаю этот путь:

if (!typeof arg == 'object') {
    //Not JSon
} else {
    //Json
}
84
задан Donal Fellows 1 January 2013 в 15:43
поделиться

9 ответов

Прежде всего, как было предложено некоторым ответом, 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 для базы данных для каждой операции, и эти правила действуют как брандмауэр перед базой данных.

11
ответ дан Akash Kava 19 August 2018 в 19:19
поделиться
  • 1
    Единичное тестирование с БД означает, что у вас есть дополнительные внешние требования для ваших тестов. Если эта БД не работает или данные очищаются, или что-то происходит с этим БД, ваши тесты не удастся. Это нежелательно. Хранилища, которые выставляют IQueryable, занимают около 2 минут для настройки. Здесь нет времени. Почему DI принял вас надолго? Все это занимает минуты. Я скажу, что все это отлично работало для модульного тестирования моих сложных запросов на моем уровне обслуживания. Это было так приятно, что вам не нужна база данных для подключения. Получение издевательской структуры от nuget заняло около минуты. Этот материал не требует времени. – user441521 26 June 2014 в 19:10
  • 2
    @ user441521 Репозитории с IQueryable 2 минуты для настройки? в каком мире вы живете, каждый запрос asp.net на нашем сайте работает в течение миллисекунд. Издевательская и фальсификация и т. Д. Придает больше сложности коду, его полной трате времени. Единичные тесты бесполезны, если единица не определена как единица бизнес-логики. – Akash Kava 27 June 2014 в 14:33

Что наиболее применимо к EF, это не шаблон репозитория. Это шаблон Facade (абстрагирование вызовов EF-методов на более простые и простые в использовании версии).

EF - это тот, который применяет шаблон репозитория (и шаблон Единицы работы). То есть, EF является тем, который абстрагирует уровень доступа к данным, так что пользователь понятия не имеет, что он имеет дело с SQLServer.

И при этом большинство «репозиториев» над EF не являются даже хорошими фасадами, поскольку они просто довольно однозначно, к одиночным методам в EF, даже до того, что они имеют одинаковые сигнатуры.

. Две причины для применения этого так называемого шаблона «Репозиторий» над EF - это упростить тестирования и установить подмножество «консервированных» звонков на него. Неплохо сами по себе, но явно не репозиторий.

1
ответ дан Aratirn 19 August 2018 в 19:19
поделиться

Для меня это простое решение с относительно небольшим количеством факторов. Факторы таковы:

  1. Хранилища предназначены для классов домена.
  2. В некоторых моих приложениях классы домена такие же, как и классы персистентности (DAL), в других они not.
  3. Когда они совпадают, EF уже предоставляет мне репозитории.
  4. EF обеспечивает ленивую загрузку и IQueryable. Мне нравится это.
  5. Тезисы / «фасад» / повторное использование репозитория над EF обычно означает потерю лени и IQueryable

Итак, если мое приложение не может оправдать # 2, отдельные модели домена и данных, тогда я обычно не буду беспокоиться о # 5.

1
ответ дан Chalky 19 August 2018 в 19:19
поделиться

ИМО как абстракция 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>)));
        }
    }
}
22
ответ дан Community 19 August 2018 в 19:19
поделиться

Существует много споров о том, какой метод является правильным, поэтому я рассматриваю его как приемлемый, поэтому я использую все, что мне больше всего нравится (в котором нет репозитория, UoW).

В EF UoW реализуется через DbContext, а DbSets - это хранилища.

Что касается того, как работать с уровнем данных, я просто непосредственно работаю над объектом DbContext, для сложных запросов я буду применять методы расширения для запроса, который можно использовать повторно.

Я считаю, что у Ayende также есть некоторые сообщения о том, как абстрагироваться от CUD-операций.

Я всегда создаю интерфейс и наследую свой контекст, поэтому я могу использовать контейнер IoC для Д.И..

6
ответ дан denis morozov 19 August 2018 в 19:19
поделиться
  • 1
    Итак, методы расширения, насколько они обширны? Допустим, мне нужно получить состояние другого объекта в моем расширении? Это моя самая большая проблема сейчас. Вы не можете показать некоторые примеры методов расширения? – Dejan.S 1 January 2013 в 19:20
  • 2
    ayende.com/blog/153473/… и ayende.com/blog/153569/… . (Это обзоры архитектуры (Framework?) Под названием s # arp lite. В основном хорошо, но он не согласен с репозиториями и абстракциями CUD). – Josh 1 January 2013 в 19:51
  • 3
    Его NHibernate основан. У вас нет примеров использования EF? И снова, когда мне нужно вызвать другую сущность, как это сделать лучше всего в статическом методе расширения? – Dejan.S 2 January 2013 в 02:15
  • 4
    Это хорошо и хорошо, пока свойство вашего объекта домена не будет увлажнено данными, которые не хранятся в вашей базе данных; или вам нужно спуститься к технологии, которая более эффективна, чем ваша раздутая ORM. OOPS! ORM просто НЕ заменяет репозиторий, это деталь реализации одной. – cdaq 29 March 2013 в 22:43

Репозиторий (или, тем не менее, один из них хочет называть его) в это время для меня, в основном касается абстрагирования слоя сохранения.

Я использую его в сочетании с объектами запроса, поэтому у меня нет связи с какой-либо конкретной технологией в моих приложениях. Кроме того, это значительно облегчает тестирование.

Итак, я имею тенденцию иметь

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 для передачи данных.

В целом, многократное повторное использование кода и простота обеспечивается этим подходом при потере того, что я могу себе представить. Любые идеи?

И большое спасибо Айенде за его большие посты и постоянную преданность делу. Его идеи здесь (объект запроса), а не мои.

1
ответ дан h.alex 19 August 2018 в 19:19
поделиться
  • 1
    Объекты сущности (ваши POCOs) не являются субъектами бизнеса / домена. И целью репозитория является отключение бизнес-уровня (ar any) от сохранения. – MikeSW 3 July 2013 в 14:34
  • 2
    Я не вижу связи. Несколько согласны с частью POCO, но все равно. Ничто не мешает вам иметь «настоящие» POCOs и по-прежнему использовать этот подход. – h.alex 3 July 2013 в 15:08
  • 3
    Сущности не обязательно должны быть немыми POCOs. Фактически, моделирование бизнес-логики в Entities - это то, что толпа DDD делает все время. Этот стиль разработки очень хорошо сочетается с NH или EF. – chris 23 July 2014 в 18:03

Шаблон репозитория представляет собой абстракцию . Цель состоит в том, чтобы уменьшить сложность и сделать остальной код неустойчивым. В качестве бонуса вы можете написать тесты 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 уровня поиска навигационной собственности. Вся причина IQueryable была изобретена в том, чтобы иметь возможность составлять любую комбинацию поиска по базе данных. Все выглядит великолепно в теории, но потребность пользователя выигрывает выше теории.

Опять же: объект с 20 + полями неправильно смоделирован. Это объект БОГА. Сломай.

Я не утверждаю, что IQueryable не был сделан для запроса. Я говорю, что это неправильно для слоя абстракции, такого как шаблон репозитория , поскольку он протекает. Нет ни одного 100% -ного провайдера LINQ To Sql (например, EF).

У всех есть конкретные вещи, связанные с реализацией, например, как использовать нетерпеливую / ленивую загрузку или как выполнять SQL-запросы «IN». Выставление IQueryable в репозитории заставляет пользователя знать все эти вещи. Таким образом, вся попытка абстрагировать источник данных - это полный сбой. Вы просто добавляете сложность, не получая никакой пользы от использования OR / M напрямую.

Либо правильно создавайте шаблон хранилища, либо просто не используйте его вообще.

(if вы действительно хотите обрабатывать большие объекты, вы можете комбинировать шаблон репозитория с шаблоном Specification . Это дает вам полную абстракцию, которая также может быть проверкой.)

43
ответ дан jgauffin 19 August 2018 в 19:19
поделиться
  • 1
    Не подвергая риску IQueryable приводит к ограниченному поиску, и люди в конечном итоге создают больше методов Get для разных типов запросов, и в конечном итоге это делает репозиторий более сложным. – Akash Kava 8 July 2013 в 09:28
  • 2
    вы вообще не рассматривали основную проблему: публикация IQueryable через репозиторий не является полной абстракцией. – jgauffin 8 July 2013 в 21:44
  • 3
    Наличие объекта запроса, который содержит всю необходимую инфраструктуру, которая будет выполняться сама по себе, - это способ выхода imo. Вы даете ему поля, которые являются поисковыми условиями, и возвращает список результатов. Внутри QO вы можете делать все, что хотите. И это интерфейс, который легко тестируется. См. Мой пост выше. Это лучшее. – h.alex 30 July 2013 в 10:05
  • 4
    Лично я считаю, что имеет смысл реализовать IQueryable & lt; T & gt; интерфейса в классе репозитория, а не раскрывать базовый набор в одном из своих членов. – dark_perfect 2 September 2013 в 20:38
  • 5
    @yat: Один репозиторий для каждого заполнителя. Но imho это не совокупный корень и совокупность таблиц , а просто агрегатный корень и агрегаты . Фактическое хранилище может использовать только одну таблицу или множество из них, т. Е. Это может быть не одно-одно сопоставление между каждым агрегатом и таблицей. Я использую репозитории для уменьшения сложности и удаления любых зависимостей базового хранилища. – jgauffin 2 December 2013 в 11:48

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
ответ дан mikalai 19 August 2018 в 19:19
поделиться
  • 1
    Что вы делаете для уровня обслуживания? – Dejan.S 2 January 2013 в 14:16
  • 2
    Контроллеры запрашивают репо для данных только для чтения, зачем добавлять дополнительный слой? Другая возможность заключается в использовании «ContentService». который все чаще становится хранилищем уровня сервиса: GetXByY и т. д. Для операций модификации - сервисы приложений - это просто абстракции по прецедентам - они свободно используют BL и repo. – mikalai 2 January 2013 в 16:08
  • 3
    Я использую уровень обслуживания для бизнес-логики. Я не уверен, что я буду следовать тому, что вы с ContentService, пожалуйста, уточните. Было бы плохой практикой делать вспомогательные классы как «сервисный уровень»? – Dejan.S 2 January 2013 в 16:42
  • 4
    «Уровень обслуживания» я имел в виду «службы приложений». Они могут использовать репозиторий и любую другую публичную часть уровня домена. «Сервисный уровень» это не плохая практика, но я бы не стал делать класс XService просто для того, чтобы предоставить List & lt; X & gt; результат. Поле комментариев кажется слишком коротким, чтобы описать услуги в деталях, извините. – mikalai 2 January 2013 в 22:34
  • 5
    Что, если, скажем, расчет корзины, и вам нужно получить параметры параметров приложения и конкретные параметры клиента для расчета, и это повторно используется в нескольких местах приложения. Как вы справляетесь с этой ситуацией? вспомогательный класс или приложение? – Dejan.S 3 January 2013 в 13:41

Я пошел по многим путям и создал множество реализаций репозиториев в разных проектах и ​​... Я бросил полотенце и отказался от него, вот почему.

Кодирование исключения

Вы кодируете для 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, поэтому у нас есть возможность использовать разные провайдеры и работать с объектными графами в хорошо связанном ключе среди многих вещей. Некоторые абстракции полезны, а некоторые нет.

Надеюсь, это поможет кому-то в интернетах где-то ...

86
ответ дан Rex Miller 19 August 2018 в 19:19
поделиться
  • 1
    Вы не создаете репозитории, чтобы иметь возможность тестировать их. Вы создаете репозитории, чтобы иметь возможность тестировать бизнес-логику . Что касается проверки работы запросов, гораздо проще написать интеграционные тесты для репозиториев, поскольку они содержат только логику, а не бизнес. – jgauffin 10 July 2014 в 08:04
  • 2
    Coding for the exception: Использование репозиториев не позволяет переключать механизм базы данных. Речь идет о том, чтобы отделить бизнес от настойчивости. – jgauffin 10 July 2014 в 08:05
  • 3
    Все это очень важные точки с большой правдой, стоящей за ними. Однако недостатка в том, что LINQ, усеянный о приложении, а не ограниченный согласованным местоположением, создает эквивалент EF вызовов SQL на страницах codebehind. Каждый запрос LINQ является потенциальной точкой обслуживания в приложении, и чем больше (и чем они более распространены), тем выше затраты на обслуживание и риски. Представьте, что добавлен флаг «удаленный» для объекта и должен найти каждое отдельное место в большом приложении, которое запрашивается сущностью, что необходимо изменить каждый ... – DVK 19 January 2016 в 19:18
  • 4
    Я думаю, что это недальновидный и измученный. Зачем вам утечка логики в репо? И если бы вы это сделали, зачем это имело бы значение? Это реализация данных. Все, что мы делаем, обходит LINQ от остальной части кода, скрывая его за репо. Вы говорите, не проверяйте его, но тогда вы используете неспособность проверить его как аргумент против этого. Поэтому сделайте репо, не подвергайте IQueryable и не проверяйте его. По крайней мере, вы можете протестировать все остальное отдельно от реализации данных. И этот 1% -ный шанс изменения db по-прежнему является огромным $ -ным. – Sinaesthetic 1 July 2016 в 16:42
  • 5
    +1 для этого ответа. Я нахожу, что нам действительно НЕ нужны репозитории с Entity Framework Core. DbSet является хранилищем , а DbContext является единицей работы . Зачем использовать шаблон репозитория, когда ORM уже делает это для нас! Для тестирования просто измените поставщика на InMemory. И делайте свои тесты! Он хорошо документирован в MSDN. – Mohammed Noureldin 22 November 2017 в 13:01
Другие вопросы по тегам:

Похожие вопросы: