Важное примечание
Все, что я объясню ниже, основано на предположении, что вы хотите научиться некоторым навыкам, которые полезны при создании больших приложений масштаба предприятия.В этих сценариях ремонтопригодность и расширяемость являются основным приоритетом.Зачем?Потому что, если вы не обращаете на них внимания, случайная сложность может легко взорвать пропорции и превратить вашу кодовую базу в одну большую чашу кода для спагетти.
Вернуться к определению
Во-первых,давайте вернемся к определению шаблона репозитория .Если вы внимательно его прочитаете, он скажет, что его цель состоит в том, чтобы содержать логику запросов, которая в вашем сценарии выполняется с помощью LINQ-to-SQL (если вы использовали ADO.NET, вашей логикой запросов был бы фактический SQL, который вы собиралисьвыполнить).
Идея этого шаблона заключается в том, что вы не хотите, чтобы потребители вашего хранилища знали или заботились о том, какой тип механизма хранения и какие способы запросов / манипуляций вы используете.
Все, что они хотят и должнымы знаем, что у них есть интерфейс, например, такой:
public interface IEmployeeRepository
{
IEnumerable<Employee> GetAllEmployees();
Employee GetEmployee(int id);
}
Это означает, что если вы вводите IQueryable где-нибудь в вашем публичном интерфейсе, это автоматически говорит им: «Эй, хотя вы говорите синтерфейс, я просто дам вам знать, что это на самом деле база данных, с которой вы на самом деле разговариваете "... которая в некотором смысле превосходит цель интерфейсов и абстракций в целом.В этом отношении вы были правы, когда говорили, что уровень Service не должен иметь доступа к базе данных.
Во-вторых, вот хорошая статья, подтверждающая мое мнение о том, что IQueryable - это утечка абстракций -это создает тесную связь между вашим приложением и механизмом хранения.
Возвращаясь к интерфейсу, который я определил выше, есть большая проблема с ним - он очень грубый.Согласно вашему примеру, в большинстве некоторых сценариев вы не хотите извлекать все данные, относящиеся к сотруднику, или все данные обо всех сотрудниках, но каждый раз разные подмножества строк и столбцов.Но мы только что избавились от гибкости, предоставляемой IQueryable, что там осталось?
Приносить только то, что вам нужно
Теперь, когда мы установили, что IQueryable не должен быть частью общедоступного интерфейса.API, есть только одна (мне известная) альтернатива, чтобы получить то, что вам нужно: отдельный метод для каждого отдельного подмножества данных о сотрудниках, которые вам нужно получить.
Например, сценарий, который вы запросили в своемвопрос выглядел бы неправдоподобно
public interface IEmployeeRepository
{
IEnumerable<BasicEmployeeData> GetBasicEmployeesData(IEnumerable<int> ids);
}
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class EntityFrameworkEmployeeRepository : IEmployeeRepository
{
public IEnumerable<BasicEmployeeData> GetBasicEmployeesData(IEnumerable<int> ids)
{
using (var context = new EmployeeContext())
{
return context.Employees.Where(e => ids.Any(id => id == e)).ToList();
// I haven't tested if .Any works or whether you should instead do .Join
// but this is orthogonal to the main question
}
}
}
Если в будущем вам понадобится указать, скажем, только идентификатор, имя, фамилию, месячный оклад и рейтинг производительности для пары сотрудников, все, что вам понадобитсянужно создать новый DTO (например, EmployeeEvaluation), добавить метод интерфейса, возвращающий IEnumerable с заданными DTO, а затем внедрить этот новый метод в ваш EntityFrameworkRepository.
Более того, если где-то еще дальше,вы хотите получить все данные, которые у вас есть для некоторых сотрудников, ваш интерфейс может выглядетьчто-то вроде этого:
public interface IEmployeeRepository
{
IEnumerable<BasicEmployeeData> GetBasicEmployeesData(IEnumerable<int> ids);
IEnumerable<EmployeeEvaluation> GetEmployeeEvaluations(IEnumerable<int> ids);
IEnumerable<Employee> GetEmployees(IEnumerable<int> ids);
IEnumerable<Employee> GetEmployees();
}
В ответ на комментарий, содержащий «В моем случае я буду также строить сервисный слой.Итак, я буду клиентом своего собственного хранилища.Так что, если это так, я не уверен, должен ли я вообще использовать шаблон репозитория. "
Под словом" клиент "я подразумеваю любой фрагмент кода, который вызывает ваш репозиторий, будь он вашим собственнымили кто-то еще. Таким образом, это может быть ваша служба, ваш контроллер, ваши тесты ИЛИ, если вы решите скомпилировать и распространить свой код как DLL, кто-то еще, кто решит его использовать.
Но, независимо от того, кто вызываетэтот код, они должны быть изолированы от знания того, что происходит за кулисами. Это также применимо, если вы владеете всей кодовой базой, как в вашем случае. Подумайте о следующем сценарии:
Когда-нибудь в будущем ваша компания решит купить какое-нибудь модное программное обеспечение, которое поможет им вести учет и ликвидировать реляционную базу данных.Чтобы ваше приложение работало, вам также придется отказаться от любого использования Entity Framework и вместо этого выполнять вызовы веб-службы для этого программного обеспечения.Теперь, если, как вы предложили, вы использовали EF напрямую во всех своих сервисах (поскольку вы не реализовали репозиторий, это единственный оставшийся вариант), на вашем слое Servuce будет много мест, где вам придетсяПерейти, удалить все ссылки на EF и преобразовать его в код, который использует новый веб-сервис.Это называется операция по дробовику , и причина в том, что вы смешиваете две вещи, которые должны быть разделены: извлечение данных и обработка данных.
Если бы вы пошли по хранилищу, единственное, что у вас было быизменить было бы хранилище (извлечение данных), тогда как вся служебная логика (обработка) осталась бы нетронутой.Прелесть развязанного кода:)
Имея это в виду, я настоятельно рекомендую вам на самом деле реализовать шаблон репозитория.Это действительно помогает вам отделить вещи в соответствии с S в SOLID.