Entity Framework новый dbContext в методе DAL без области использования ()

Я немного знаком с Entity Framework для некоторых простых проектов, но теперь я хочу углубиться и написать более качественный код.

Существует множество тем, в которых говорится о том, следует ли использовать статические методы в DAL или нет. На данный момент я больше на стороне людей, которые думают, что да, мы можем использовать методы статики.

Но я все еще думаю, хороши некоторые практики или нет.

Многие люди делают так:

    public IList<Person> GetAll()
    {
        using (var dbContext = new MyDbContext())
        {
            return dbContext.Persons.ToList();
        }
    }

Но мне интересно, является ли это хорошей практикой:

    public static IQueryable<Person> GetAll()
    {
        var dbContext = new MyDbContext();
        return dbContext.Persons;
    }

Цель состоит в том, чтобы использовать только статические методы в статическом классе, поскольку я думаю, что это законно, потому что этот класс является просто DAL, в нем никогда не будет никакого свойства. Мне также нужно сделать это вместо использования области использования (), чтобы избежать удаления контекста, поскольку этот метод возвращает IQueryable.

Я уверен, что некоторые люди уже думают: «Господи, нет, ваш контекст никогда не будет удален», поэтому, пожалуйста, прочитайте эту статью: http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html

Я пробовал сам, и да, контекст удаляется только тогда, когда он мне больше не нужен. Повторяю, цель здесь — использовать статические методы, поэтому я не могу использовать свойство dbContext, экземпляр которого создается конструктором.

Так почему же люди всегда используют область использования using()?

Это плохая практика, чтобы сделать это, как я хотел бы?

Еще один бонусный вопрос: где атрибут [NotMapped] в EF6? Я проверил как System.ComponentModel.DataAnnotations, так и System.ComponentModel.DataAnnotations.Schema, но не могу найти его, этот атрибут не распознается компилятором.

Спасибо за ваши ответы


person Jérôme MEVEL    schedule 19.08.2014    source источник
comment
Я не знаю почему, stackoverflow не позволяет мне написать Hello all в начале моей темы, не подумайте, что я невежлив :-)   -  person Jérôme MEVEL    schedule 19.08.2014
comment
Добавление оператора using() эффективно отключает ленивую загрузку, что может быть полезно...   -  person DavidG    schedule 19.08.2014
comment
Планируете ли вы всегда создавать новый экземпляр dbcontext для каждого GetAll ? Дело не только в соединении, но и в Local кеше, если у вас много живого dbcontext, кеш будет потреблять много ресурсов   -  person Yuliam Chandra    schedule 19.08.2014
comment
@DavidG Я не знал об этом, спасибо за советы. Но в любом случае, я всегда предпочитаю отключать его по умолчанию в конструкторе контекста this.Configuration.LazyLoadingEnabled = false;   -  person Jérôme MEVEL    schedule 19.08.2014
comment
@YuliamChandra Если я понимаю статью, которую я дал, мне не нужно об этом заботиться, потому что все dbcontexts будут автоматически удаляться, когда они больше не используются. Я что-то неправильно понял?   -  person Jérôme MEVEL    schedule 19.08.2014
comment
когда в локальном кеше есть объект, привязанный к элементу управления, удаление не произойдет   -  person Yuliam Chandra    schedule 19.08.2014
comment
@YuliamChandra, у вас есть ссылка, объясняющая это, пожалуйста? когда я добавляю часы во время отладки, кажется, что dbContext уничтожается сборщиком мусора. Он говорит мне, что имя «dbContext» не существует в текущем контексте.   -  person Jérôme MEVEL    schedule 19.08.2014
comment
здесь В большинстве случаев следует создать экземпляр ObjectContext в операторе using (Using…End Using в Visual Basic). Это может повысить производительность, гарантируя, что ресурсы, связанные с контекстом объекта, удаляются автоматически, когда код выходит из блока инструкций. Однако, когда элементы управления привязаны к объектам, управляемым контекстом объекта, экземпляр ObjectContext должен поддерживаться до тех пор, пока требуется привязка, и удаляться вручную.   -  person Yuliam Chandra    schedule 19.08.2014
comment
Хорошо, спасибо, но если я буду следовать этой хорошей практике, невозможно будет вернуть IQueryable...   -  person Jérôme MEVEL    schedule 19.08.2014
comment
@Koenigs Почему ты хочешь вернуть IQuerable<T>? Это стоит вернуть только в том случае, если вы собираетесь выполнять дальнейшие выражения для уточнения сгенерированного SQL. В противном случае список реализует интерфейс IEnumerable<T>, против которого также работает LINQ.   -  person Justin    schedule 19.08.2014
comment
Я не понимаю одержимость статическими методами. Зачем вам статические методы? Для производительности? Как вы думаете, какой процент времени обработки вы сэкономите, сделав свои методы статическими? У вас есть вызовы БД, которые занимают 10-200 мс, а сколько времени занимает выполнение виртуального метода? Вы теряете все преимущества ООП и ради чего? Неужели мы проделали весь этот путь только для того, чтобы кодировать так же, как реализуем на C?   -  person Mick    schedule 20.08.2014
comment
Это не вопрос производительности, DAL — это просто набор методов, которые предоставляют вам способ доступа к вашей БД. Некоторые люди предпочитают хранить уникальный dbContext как частное свойство, а некоторые считают, что лучше каждый раз использовать оператор using(). Но ожидайте этого необязательного свойства, вам не нужны никакие другие, и если вам потребуются другие, это явно проблема. Вот почему, с моей точки зрения, создание экземпляра вашего класса DAL не имеет никакого смысла.   -  person Jérôme MEVEL    schedule 20.08.2014
comment
Имея выбор между использованием и отказом от использования объектов, я использую объекты почти каждый раз, потому что я объектно-ориентированный программист и дизайнер. Я реализую шаблон репозитория, используя SOLID en.wikipedia.org/wiki/SOLID_(object- ориентированный_дизайн), что невозможно сделать со статическими классами и статическими методами. У меня были бы серьезные опасения по поводу любой существенной бизнес-логики, которая была определена в статических классах и методах.   -  person Mick    schedule 21.08.2014


Ответы (1)


Следуя шаблону Репозиторий, IQueryable<T> в любом случае никогда не будет возвращен.

Правильный шаблон репозитория

Кроме того, ваши репозитории зависят от вашего Дбконтекст. Допустим, вам нужно работать с клиентами в системе учета.

Клиент

public class Customer {
    public int Id { get; protected set; }
    public string GivenName { get; set; }
    public string Surname { get; set; }
    public string Address { get; set; }
}

Репозиторий клиентов

public class CustomerRepository {
    public CustomerRepository(DbContext context) { 
        if (context == null) throw new ArgumentNullException("context");
        this.context = context; 
    }

    public IList<Customer> GetAll() { return context.Customers.ToList(); }
    public IList<Invoice> GetInvoicesFor(Customer customer) {
        return context.Invoices
            .Where(invoice => invoice.Customer.Id == customer.Id)
            .ToList();
    }

    private readonly DbContext context;
}

Так что на самом деле, чтобы ответить на ваш вопрос более кратко и точно, я думаю, что ни один из подходов не является хорошим. Я бы предпочел использовать DbContext для каждой задачи. Когда вы получаете доступ, скажем, к функциям Customers Management, затем создайте экземпляр одного DbContext, который будет общим для всех ваших необходимых репозиториев, а затем удалите этот самый DbContext. strong> после выхода из этого набора функций. Таким образом, вам не нужно будет использовать операторы Using, и ваши контексты будут адекватно управляться.

Вот еще один краткий и простой хороший справочник по шаблону Repository:

Репозиторий (Мартин Фаулер)

В ответ на комментарии ОП

Но на самом деле дело в том, что я не хочу следовать шаблону репозитория. Люди говорят: «А что, если ваш источник данных изменится?» Я хочу ответить, а если это никогда не изменится? Какой смысл иметь такой мощный класс, но не использовать его на случай, если однажды поставщик базы данных может измениться

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

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

Что касается вашего мнения о том, изменится ли когда-нибудь источник данных? Никто не может это предсказать. Скорее всего, он никогда не изменится в большинстве систем, над которыми я работал. Изменение базы данных, скорее всего, будет замечено через 10 лет первоначальной разработки в целях модернизации. Однако в тот же день вы поймете, как шаблон репозитория экономит ваше время и избавляет от головной боли по сравнению со тесно связанным кодом. Я работаю с тесно связанным кодом из устаревших систем и пользуюсь преимуществами. Профилактика лучше лечения.

Но, пожалуйста, давайте сосредоточимся на создании экземпляра dbContext в методах без оператора using(). Это действительно плохо? Я также имею в виду, когда мы внедряем контекст в конструктор, мы не обрабатываем dispose(), мы позволяем инфраструктуре сущностей делать это, и она довольно хорошо с этим справляется.

Нет, не обязательно плохо не использовать операторы using, если вы избавляетесь от всех ненужных ресурсов, если они больше не используются. Операторы using служат этой цели, делая это автоматически, а не заботясь об этом.

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

Допустим, у вас есть функции управления клиентами. В них вам также могут потребоваться счета-фактуры для этого клиента вместе с историей транзакций. Единый контекст данных должен использоваться для всего доступа к данным, пока пользователь работает в бизнес-контексте управления клиентами. Затем вы должны ввести один DbContext в свою функцию управления клиентами. Этот самый DbContext должен быть общим для всех репозиториев, используемых для доступа к вашему источнику данных.

Как только пользователь выходит из функций управления клиентами, DbContext должен быть утилизирован соответствующим образом, так как это может вызвать утечку памяти. Неверно полагать, что пока он больше не используется, все убирается мусором. Вы никогда не знаете, как .NET Framework управляет своими ресурсами и сколько времени потребуется, чтобы избавиться от вашего DbContext. Вы только знаете, что когда-нибудь это может быть как-то утилизировано.

Если DbContext удаляется сразу после выполнения доступа к данным, вам придется создавать новый экземпляр каждый раз, когда вам нужно получить доступ к базовому источнику данных. Это вопрос здравого смысла. Вы должны определить контекст, в котором будет использоваться DbContext, сделать его общим для всех идентифицированных ресурсов и удалить, как только он больше не нужен. В противном случае это может привести к утечке памяти и другим подобным проблемам.

В ответ на комментарий Мика

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

Я не согласен всегда возвращать IQueryable<T> через Repository, иначе зачем иметь несколько методов? Чтобы получить данные в вашем репозитории, можно просто сделать:

public class Repository<T> where T : class {
    public Repository(DbContext dataContext) { context = dataContext; }

    public IQueryable<T> GetAll() { return context.Set<T>(); }

    private readonly DbContext context;
}

и размещайте предикаты повсюду в вашем коде, чтобы фильтровать данные в соответствии с потребностями представлений или чем-то еще. Когда придет время изменить критерий фильтра или что-то подобное, вам придется просмотреть весь свой код, чтобы убедиться, что никто не использовал фильтр, который был на самом деле неожиданным и мог привести к неправильному поведению системы.

Кстати, я понимаю вашу точку зрения и могу признать, что по некоторым причинам, описанным в вашем комментарии, может быть полезно вернуть IQueryable<T>. Кроме того, мне просто интересно, для чего это нужно, поскольку ответственность репозитория состоит в том, чтобы предоставить классу все, что ему нужно для получения своих информационных данных. Поэтому, если нужно передать IQueryable<T>s в другой репозиторий, мне это кажется странным, как если бы никто не исследовал полностью все возможные способы извлечения данных. Если для обработки другого запроса нужны какие-то данные, ленивая загрузка может это сделать, и нет необходимости возвращать IQueryables. Судя по названию, IQueryable предназначены для выполнения запросов, а Repository отвечает за доступ к данным, то есть за выполнение запросов.

person Will Marcouiller    schedule 19.08.2014
comment
Но на самом деле дело в том, что я не хочу следовать шаблону репозитория. Люди говорят, что если ваш источник данных изменится? Я хочу ответить, а если это никогда не изменится? Какой смысл иметь такой мощный класс, но не использовать его на тот случай, если однажды поставщик базы данных может измениться... - person Jérôme MEVEL; 20.08.2014
comment
Но, пожалуйста, давайте сосредоточимся на создании экземпляра dbContext в методах без оператора using(). Это действительно плохо? Я также имею в виду, когда мы вводим контекст в конструктор, мы не обрабатываем dispose(), мы позволяем инфраструктуре сущностей делать это, и она довольно хорошо с этим справляется. - person Jérôme MEVEL; 20.08.2014
comment
Возврат IQueryable и реализация шаблона репозитория не исключают друг друга. Если каким-то образом возврат IEnumerable дает вам лучший дизайн, чем возврат IQueryable (который наследует IEnumerable), я думаю, у вас есть фундаментальное непонимание того, что такое хороший дизайн - person Mick; 20.08.2014
comment
@Mick: Опять же, это зависит. Я согласен с тем, что IQueryable<T> и репозитории не обязательно исключают друг друга. Кроме того, четко определенные бизнес-требования с точки зрения возможностей поиска не должны требовать IQueryable в результате, поскольку дальнейшая фильтрация не требуется. Кроме того, если это так, все еще можно фильтровать список. Тем не менее, списки лучше возвращать, в зависимости от того, насколько хорошо определены бизнес-требования. Однако базовый репозиторий также может возвращать IQueryable для лучшей расширяемости. - person Will Marcouiller; 20.08.2014
comment
Я использую IQueryable все время. Например. Я прошу IQueryable местоположений из репозитория местоположений с указанием критериев. Затем я передаю этот IQueryable в EmployeeRepository, запрашивая список сотрудников в этих местоположениях. Почему IQueryable? Потому что я могу использовать его для получения списка местоположений, а также могу передать его в репозиторий сотрудников, который может включать бизнес-логику из LocationRepository для создания одного SQL-запроса для возврата мне сотрудников. Кажется довольно разумным и разумным нет? - person Mick; 21.08.2014
comment
Я бы пошел дальше и предложил вам всегда возвращать IQueryable, чтобы вы могли повторно использовать этот результат, передавая его в другие вызовы в ваших репозиториях. Извините, но ваш аргумент мне абсолютно непонятен. Репозитории не предназначены для того, чтобы быть автономными универсальными магазинами, их следует использовать для разбиения логики на небольшие, понятные, инкапсулированные, легко поддерживаемые фрагменты. - person Mick; 21.08.2014
comment
В конце концов, есть много способов сделать одно и то же. Возьмите 20 программистов/архитекторов, и у вас, вероятно, будет около 20 способов сделать то же самое. Я основывал свой ответ на сообщении в блоге JGauffin, который, вероятно, лучше выразил свои аргументы. Репозиторий должен выражать функциональные бизнес-требования в отношении доступа к данным. Если вам нужно перейти из одного репозитория в другой с помощью IQueryables, возможно, есть какие-то недостатки в способе выполнения бизнес-требований, а возможно и нет, все зависит от того, как настроена окружающая архитектура. - person Will Marcouiller; 21.08.2014