在面向对象设计模式中,仓储模式(Repository Pattern)和服务层(Service Layer)有何区别?
我正在开发一个ASP.NET MVC 3应用程序,并尝试理解这些设计模式,但我的大脑还没有完全理解它们!
在面向对象设计模式中,仓储模式(Repository Pattern)和服务层(Service Layer)有何区别?
我正在开发一个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);
}
虽然 repository 的 List()
方法返回所有用户,但 IUserService 的 ListUsers()
可能仅返回该用户可以访问的用户。
在 ASP.NET MVC + EF + SQL SERVER 中,我有这样的通信流程:
Views <- Controllers -> Service 层 -> Repository 层 -> EF -> SQL Server
Service 层 -> Repository 层 -> EF 这一部分操作模型。
Views <- Controllers -> Service 层 这一部分操作视图模型。
编辑:
以 /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...}
}
);
}
}
onBeforeBuildBrowseQuery
等方法与存储库对象交互,并可以使用查询构建器来更改查询的位置。 - webstackdev被接受的答案(并得到数百个点赞)存在一个主要缺陷。我想在评论中指出这一点,但它只会在30多条评论中被埋没,所以我在这里指出。
我接手了一个企业应用程序,它就是这样构建的,我的初始反应是“什么鬼”?ViewModel放在服务层里?我不想改变约定,因为已经进行了多年的开发,所以我继续返回ViewModel。但当我们开始使用WPF后,它变成了噩梦。我们(团队开发人员)总是说:“哪个ViewModel?真正的那个(我们为WPF编写的那个),还是服务层的那个?”它们是为Web应用程序编写的,甚至有一个IsReadOnly标志来禁用UI中的编辑。由于一个词,即ViewModel,造成了重大的缺陷和问题!!
在您犯同样的错误之前,以下是除了我上面的故事之外的一些更多原因:
从服务层返回ViewModel是一件非常不好的事情。这就像说:
如果你想使用这些服务,你最好使用MVVM,并且这是你需要使用的ViewModel。糟糕!
服务正在假设它们将在某个UI中显示。如果它被非UI应用程序(例如Web服务或Windows服务)使用会怎样呢?
那甚至不是一个真正的视图模型。一个真正的视图模型具有可观察性、命令等功能。那只是一个名字不好的POCO。(请看上文中我提到名字的原因。)
消费应用程序最好是一个表示层(视图模型由该层使用),并且最好理解C#。又是一次痛苦!
请不要这样做!
仓储层用于访问数据库,并帮助扩展对数据库的CRUD操作。而服务层包含应用程序的业务逻辑,可以使用仓储层来实现涉及数据库的某些逻辑。在应用程序中,最好有单独的仓储层和服务层。拥有独立的仓储层和服务层使代码更具模块化,将数据库与业务逻辑解耦。
通常,一个仓库被用作搭建框架来填充实体 - 服务层将会发起请求。很可能你会把仓库放在你的服务层之下。
IRepository<>
绑定到GenericRepository<>
。该答案非常古老。我认为最好的解决方案是将所有存储库组合成一个名为“UnitOfWork”的类。它应包含每种类型的存储库和一个名为“SaveChanges”的方法。所有存储库都应共享一个EF上下文。 - LukLed