Следуя шаблону Репозиторий, 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 в другой репозиторий, мне это кажется странным, как если бы никто не исследовал полностью все возможные способы извлечения данных. Если для обработки другого запроса нужны какие-то данные, ленивая загрузка может это сделать, и нет необходимости возвращать IQueryable
s. Судя по названию, IQueryable
предназначены для выполнения запросов, а Repository
отвечает за доступ к данным, то есть за выполнение запросов.
person
Will Marcouiller
schedule
19.08.2014
using()
эффективно отключает ленивую загрузку, что может быть полезно... - person DavidG   schedule 19.08.2014GetAll
? Дело не только в соединении, но и вLocal
кеше, если у вас много живого dbcontext, кеш будет потреблять много ресурсов - person Yuliam Chandra   schedule 19.08.2014IQuerable<T>
? Это стоит вернуть только в том случае, если вы собираетесь выполнять дальнейшие выражения для уточнения сгенерированного SQL. В противном случае список реализует интерфейсIEnumerable<T>
, против которого также работает LINQ. - person Justin   schedule 19.08.2014