Это похоже на шаблон репозитория и обычно становится беспорядочным, если вы хотите попытаться "спрятать" EF / DbContext от вызывающего кода.
Пара вариантов, которые вы можете рассмотреть:
- Вниз по дебету сложности: используйте
params Expression<Func<TEntity, object>>[] includes
в ваших подходящих методах репозитория, а затем будьте готовы к тому, чтобы также передавать выражения OrderBy и значения нумерации страниц, если вы хотите вернуть несколько сущностей.
- С помощью зеркала простоты: используйте IQueryable в качестве типа возврата и позволяйте потребителям обрабатывать значения IncB, OrderBy's, Count / Any / Skip / Take / First / ToList и
.Select()
по мере необходимости.
Вариант 1:
public Order GetById(int id, params Expression<Func<Order, object>>[] includes)
{
var query = db.Orders.Where(x => x.ID == id);
// This part can be moved into an extension method or a base repository method.
if(includes.Any)
includes.Aggregate(query, (current, include) =>
{
current.Include(include);
}
// Don't use .FirstOrDefault() If you intend for 1 record to be returned, use .Single(). If it really is optional to find, .SingleOrDefault()
return query.Single();
}
//ToDo
public IEnumerable<Order> GetOrders(/* criteria?, includes?, order by?, (ascending/descending) pagination? */)
{ }
// or
public IEnumerable<Order> GetOrdersByCustomer(/* includes?, order by?, (ascending/descending) pagination? */)
{ }
// plus..
public IEnumerable<Order> GetOrdersByDate(/* includes?, order by?, (ascending/descending) pagination? */)
{ }
public bool CustomerHasOrders(int customerId)
{ }
public bool OrderExists(int id)
{ }
public int OrdersOnDate(DateTime date)
{ }
// etc. etc. etc.
Имейте в виду, что это не обрабатывает пользовательский порядок по пунктам, и то же самое будет необходимо для методов, которые возвращают списки объектов. Вашему хранилищу также потребуется предоставить методы для .Any()
(DoesExist), потому что каждый любит , проверяя #null при каждом возврате. :) Также .Count()
.
Вариант 2:
public IQueryable<Order> GetById(int id)
{
return db.Orders.Where(x => x.ID == id);
}
public IQueryable<Order> GetOrders()
{
return db.Orders.AsQueryable();
}
Вызывающие могут получить Linq и .Include()
, что им нужно, перед вызовом .Single()
, или сделать .Any()
. Им может не понадобиться весь граф сущностей, поэтому они могут .Select()
от сущности и связанных сущностей без .Include()
для составления и выполнения более эффективного запроса для заполнения ViewModel / DTO. GetById может использоваться в нескольких местах, поэтому мы можем уменьшить дублирование и поддерживать его в хранилище. Нам не нужны все сценарии фильтрации и т. Д., Вызывающие абоненты могут вызывать GetOrders, а затем фильтровать по своему усмотрению.
Зачем беспокоиться о хранилище, если оно просто возвращает DBSets?
- Централизация низкоуровневой фильтрации данных. Например, если вы используете Soft Deletes (IsActive) или работаете с несколькими арендаторами, или явная авторизация. Эти общие правила могут быть централизованы на уровне репозитория, а не запоминаться везде, где затрагивается DbSet.
- Тестирование проще. В то время как вы можете смоделировать DbContext или указать его на базу данных в памяти, макетировать хранилище, возвращающее IQueryable, проще. (Просто заполните
List<TEntity>
и верните .AsQueryable()
.
- Хранилище обрабатывает создание и удаление. Создать, чтобы служить фабрикой, чтобы гарантировать, что все необходимые данные и отношения установлены для жизнеспособного объекта. Удалить, чтобы обрабатывать сценарии мягкого удаления, каскады / аудиты и т. Д. Помимо того, что БД обрабатывает за кадром.