В чем разница между шаблоном репозитория и уровнем сервиса в шаблонах проектирования ООП?
Я работаю над приложением ASP.NET MVC 3 и пытаюсь понять эти шаблоны проектирования, но мой мозг просто не понимает этого ... пока !!
Ответы:
Уровень репозитория дает вам дополнительный уровень абстракции над доступом к данным. Вместо того, чтобы писать
var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();
чтобы получить один элемент из базы данных, вы используете интерфейс репозитория
public interface IRepository<T>
{
IQueryable<T> List();
bool Create(T item);
bool Delete(int id);
T Get(int id);
bool SaveChanges();
}
и звоните Get(id)
. Уровень репозитория предоставляет основные операции CRUD .
Уровень сервиса предоставляет бизнес-логику, которая использует репозиторий. Пример услуги может выглядеть так:
public interface IUserService
{
User GetByUserName(string userName);
string GetUserNameByEmail(string email);
bool EditBasicUserData(User user);
User GetUserByID(int id);
bool DeleteUser(int id);
IQueryable<User> ListUsers();
bool ChangePassword(string userName, string newPassword);
bool SendPasswordReminder(string userName);
bool RegisterNewUser(RegisterNewUserModel model);
}
В то время как List()
метод репозитория возвращает всех пользователей, ListUsers()
IUserService может возвращать только тех, к которым у пользователя есть доступ.
В ASP.NET MVC + EF + SQL SERVER у меня есть такой поток коммуникации:
Представления <- Контроллеры -> Уровень обслуживания -> Уровень репозитория -> EF -> SQL Server
Уровень обслуживания -> Уровень репозитория -> EF Эта часть работает с моделями.
Представления <- Контроллеры -> Сервисный уровень Эта часть работает с моделями представления.
РЕДАКТИРОВАТЬ:
Пример потока для / Orders / ByClient / 5 (мы хотим видеть заказ для конкретного клиента):
public class OrderController
{
private IOrderService _orderService;
public OrderController(IOrderService orderService)
{
_orderService = orderService; // injected by IOC container
}
public ActionResult ByClient(int id)
{
var model = _orderService.GetByClient(id);
return View(model);
}
}
Это интерфейс для обслуживания заказов:
public interface IOrderService
{
OrdersByClientViewModel GetByClient(int id);
}
Этот интерфейс возвращает модель представления:
public class OrdersByClientViewModel
{
CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
IEnumerable<OrderViewModel> Orders { get; set; }
}
Это реализация интерфейса. Он использует классы моделей и репозиторий для создания модели представления:
public class OrderService : IOrderService
{
IRepository<Client> _clientRepository;
public OrderService(IRepository<Client> clientRepository)
{
_clientRepository = clientRepository; //injected
}
public OrdersByClientViewModel GetByClient(int id)
{
return _clientRepository.Get(id).Select(c =>
new OrdersByClientViewModel
{
Cient = new ClientViewModel { ...init with values from c...}
Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}
}
);
}
}
IRepository<>
к GenericRepository<>
вашей библиотеке МОК. Это очень старый ответ. Я думаю, что лучшим решением будет объединить все репозитории в один класс под названием UnitOfWork
. Он должен содержать репозиторий всех типов и один вызываемый метод SaveChanges
. Все репозитории должны использовать один контекст EF.
Как сказал Карнотавр, репозиторий отвечает за отображение ваших данных из формата хранения на ваши бизнес-объекты. Он должен обрабатывать как чтение, так и запись данных (удаление, обновление тоже) из хранилища и в него.
С другой стороны, цель уровня обслуживания - объединить бизнес-логику в одном месте, чтобы способствовать повторному использованию кода и разделению проблем. На практике это обычно означает для меня при создании сайтов Asp.net MVC, что у меня есть эта структура
[Контроллер] вызывает [службы], который вызывает [репозиторий (и)]
Один принцип, который я нашел полезным, - это сведение к минимуму логики в контроллерах и репозиториях.
В контроллерах это потому, что это помогает мне оставаться СУХИМ. Очень часто мне нужно использовать ту же фильтрацию или логику где-то еще, и если я поместил ее в контроллер, я не могу ее повторно использовать.
В репозиториях это потому, что я хочу иметь возможность заменить свое хранилище (или ORM), когда появится что-то лучшее. И если у меня есть логика в репозитории, мне нужно переписать эту логику, когда я меняю репозиторий. Если мой репозиторий возвращает только IQueryable, а служба выполняет фильтрацию, с другой стороны, мне нужно будет только заменить сопоставления.
Например, я недавно заменил несколько своих репозиториев Linq-To-Sql на EF4, и те, в которых я оставался верным этому принципу, можно было заменить за считанные минуты. В тех случаях, когда у меня была логика, это был вопрос часов.
onBeforeBuildBrowseQuery
и могут использовать построитель запросов для изменения запроса.
Принятый ответ (за который проголосовали сотни раз) имеет серьезный недостаток. Я хотел указать на это в комментарии, но это просто будет похоронено там в 30 комментариях, так что указывайте здесь.
Я взял на себя корпоративное приложение, которое было построено таким образом, и моя первая реакция была WTH ? ViewModels в сервисном слое? Я не хотел менять соглашение, потому что на это ушли годы разработки, поэтому я продолжил возвращать ViewModels. Когда мы начали использовать WPF, это превратилось в кошмар. Мы (команда разработчиков) всегда говорили: какая ViewModel? Настоящий (тот, который мы написали для WPF) или сервисный? Они были написаны для веб-приложения и даже имели флаг IsReadOnly для отключения редактирования в пользовательском интерфейсе. Главный, главный недостаток и все из-за одного слова: ViewModel !!
Прежде чем вы совершите ту же ошибку, вот еще несколько причин в дополнение к моей истории выше:
Возврат ViewModel из уровня сервиса - это просто нет. Это как сказать:
Если вы хотите использовать эти сервисы, вам лучше использовать MVVM, и вот модель ViewModel, которую вам нужно использовать. Ой!
Сервисы предполагают, что они будут где-то отображаться в пользовательском интерфейсе. Что, если он используется приложением без пользовательского интерфейса, например веб-службами или службами Windows?
Это даже не настоящая ViewModel. Настоящая ViewModel имеет наблюдаемость, команды и т.д. Это просто POCO с плохой репутацией. (См. Мою историю выше, чтобы узнать, почему имена имеют значение.)
Потребляющим приложением лучше быть уровнем представления (на этом уровне используются модели представления), и оно лучше понимает C #. Еще Ой!
Пожалуйста, не делай этого!
Обычно репозиторий используется в качестве каркаса для заполнения ваших сущностей - уровень сервиса выходит и отправляет запрос. Вполне вероятно, что вы поместите репозиторий под свой уровень обслуживания.
Уровень репозитория реализуется для доступа к базе данных и помогает расширить операции CRUD в базе данных. В то время как уровень сервиса состоит из бизнес-логики приложения и может использовать уровень репозитория для реализации определенной логики, связанной с базой данных. В приложении лучше иметь отдельный уровень репозитория и уровень сервиса. Наличие отдельных уровней репозитория и сервисов делает код более модульным и отделяет базу данных от бизнес-логики.