В чем разница между шаблоном репозитория и уровнем сервиса в шаблонах проектирования ООП?
Я работаю над приложением ASP.NET MVC 3 и пытаюсь понять эти шаблоны проектирования, но мой мозг просто не понимает этого ... пока !!
В чем разница между шаблоном репозитория и уровнем сервиса в шаблонах проектирования ООП?
Я работаю над приложением 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...}
}
);
}
}
return _clientRepository.Get(id).Select(c => new OrdersByClientViewModel (c));
- person Ryan; 20.02.2011
var model = _prderService.GetByClient(id);
должно быть: var model = _orderService.GetByClient(id);
- person Lasse Christiansen; 11.12.2011
IRepository<>
к GenericRepository<>
в своей библиотеке IOC. Это очень старый ответ. Я думаю, что лучшим решением будет объединить все репозитории в один класс под названием UnitOfWork
. Он должен содержать репозиторий всех типов и один метод с именем SaveChanges
. Все репозитории должны использовать один контекст EF.
- person LukLed; 24.07.2013
I think the best solution is to combine all repositories in one class called UnitOfWork
Зачем это нужно? DbContext
EF уже является UOW.
- person Halter; 13.12.2016
Get(int id)
. DbContext не предоставляет вам этот метод. Вы должны подать Where
, а затем взять FirstOrDefault
. Хотя написать context.Collection.Where(item => item.Id == id).FirstOrDefault()
не составляет большой проблемы, я предпочитаю repository.Get(id)
. В DbContext
много функций, и я видел, как разработчики делали с ним сумасшедшие вещи, и редко это было оправдано. Ограниченный доступ к DbContext
с использованием IRepository
достаточно в 99% случаев и упрощает код.
- person LukLed; 14.12.2016
Get(id)
). Нет никакого вреда. Выставлять общий репозиторий как службу неправильно, но использовать его в качестве помощника вполне нормально.
- person LukLed; 14.12.2016
new
, слишком много ToList
, слишком много virtual
, слишком много try catch
и слишком много IEnumerable
.
- person LukLed; 14.12.2016
_clientRepository.Get(id)
return single entity не будет иметь метода Select ().
- person Cheung; 16.06.2017
ListUsers()
может возвращать отфильтрованных пользователей. Пример: return _repository.List().Where(u => u.BossId == currentUserId)
.
- person LukLed; 20.06.2017
viewModel
в этой строке var model = _orderService.GetByClient(id);
. Однако на самом деле сервис возвращает viewModel
в _orderService
.
- person StepUp; 23.02.2018
Как сказал Карнотавр, репозиторий отвечает за отображение ваших данных из формата хранения на ваши бизнес-объекты. Он должен обрабатывать как чтение, так и запись данных (удаление, обновление тоже) из хранилища и в него.
С другой стороны, цель уровня обслуживания - объединить бизнес-логику в одном месте, чтобы способствовать повторному использованию кода и разделению проблем. На практике это обычно означает для меня при создании сайтов Asp.net MVC, что у меня есть эта структура
[Контроллер] вызывает [службы], который вызывает [репозиторий (и)]
Один принцип, который я нашел полезным, - это сведение к минимуму логики в контроллерах и репозиториях.
В контроллерах это потому, что это помогает мне оставаться СУХИМ. Очень часто мне нужно использовать ту же фильтрацию или логику где-то еще, и если я поместил ее в контроллер, я не смогу использовать ее повторно.
В репозиториях это потому, что я хочу иметь возможность заменить свое хранилище (или ORM), когда появится что-то лучшее. И если у меня есть логика в репозитории, мне нужно переписать эту логику при изменении репозитория. Если мой репозиторий возвращает только IQueryable, а служба выполняет фильтрацию, с другой стороны, мне нужно будет только заменить сопоставления.
Например, я недавно заменил несколько своих репозиториев Linq-To-Sql на EF4, и те, в которых я остался верен этому принципу, можно было заменить за считанные минуты. В тех случаях, когда у меня была некоторая логика, это был вопрос часов.
onBeforeBuildBrowseQuery
, и могут использовать построитель запросов для изменения запроса.
- person webstackdev; 24.01.2019
Принятый ответ (за который проголосовали сотни раз) имеет серьезный недостаток. Я хотел указать на это в комментарии, но это просто будет похоронено там в 30 комментариях, так что указывайте здесь.
Я взял на себя корпоративное приложение, которое было построено таким образом, и моя первая реакция была WTH? ViewModels в сервисном слое? Я не хотел менять соглашение, потому что на него ушли годы разработки, поэтому я продолжил возвращать ViewModels. Когда мы начали использовать WPF, это превратилось в кошмар. Мы (команда разработчиков) всегда говорили: какая ViewModel? Настоящий (тот, который мы написали для WPF) или сервисный? Они были написаны для веб-приложения и даже имели флаг IsReadOnly для отключения редактирования в пользовательском интерфейсе. Главный, серьезный недостаток и все из-за одного слова: ViewModel !!
Прежде чем вы совершите ту же ошибку, вот еще несколько причин в дополнение к моей истории выше:
Возврат ViewModel из уровня сервиса - это просто нет. Это как сказать:
Если вы хотите использовать эти службы, вам лучше использовать MVVM, и вот модель ViewModel, которую вам нужно использовать. Ой!
Службы предполагают, что они будут где-то отображаться в пользовательском интерфейсе. Что, если он используется приложением без пользовательского интерфейса, например веб-службами или службами Windows?
Это даже не настоящая ViewModel. Настоящая ViewModel имеет наблюдаемость, команды и т. д. Это просто POCO с плохой репутацией. (См. Мою историю выше, чтобы узнать, почему имена имеют значение.)
Потребляющим приложением лучше быть уровнем представления (на этом уровне используются модели представления), и оно лучше понимает C #. Еще одна ох!
Пожалуйста, не делай этого!
Обычно репозиторий используется в качестве каркаса для заполнения ваших сущностей - уровень сервиса выходит и отправляет запрос. Вполне вероятно, что вы поместите репозиторий под свой уровень обслуживания.
Слой репозитория реализуется для доступа к базе данных и помогает расширить операции CRUD в базе данных. В то время как уровень сервиса состоит из бизнес-логики приложения и может использовать уровень репозитория для реализации определенной логики, связанной с базой данных. В приложении лучше иметь отдельный уровень репозитория и уровень сервиса. Наличие отдельных уровней репозитория и сервисов делает код более модульным и отделяет базу данных от бизнес-логики.