Câu trả lời:
Lớp lưu trữ cung cấp cho bạn mức độ trừu tượng bổ sung đối với truy cập dữ liệu. Thay vì viết
var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();
để có được một mục từ cơ sở dữ liệu, bạn sử dụng giao diện kho lưu trữ
public interface IRepository<T>
{
IQueryable<T> List();
bool Create(T item);
bool Delete(int id);
T Get(int id);
bool SaveChanges();
}
và gọi Get(id)
. Lớp lưu trữ trưng bày các hoạt động CRUD cơ bản .
Lớp dịch vụ trưng ra logic nghiệp vụ, sử dụng kho lưu trữ. Dịch vụ ví dụ có thể trông như:
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);
}
Mặc dù List()
phương thức kho lưu trữ trả về tất cả người dùng, nhưng ListUsers()
IUserService chỉ có thể trả về những người dùng, người dùng có quyền truy cập.
Trong ASP.NET MVC + EF + SQL SERVER, tôi có luồng giao tiếp này:
Lượt xem <- Bộ điều khiển -> Lớp dịch vụ -> Lớp kho lưu trữ -> EF -> Máy chủ SQL
Lớp dịch vụ -> Lớp lưu trữ -> EF Phần này hoạt động trên các mô hình.
Lượt xem <- Bộ điều khiển -> Lớp dịch vụ Phần này hoạt động trên các mô hình xem.
BIÊN TẬP:
Ví dụ về luồng cho / Đơn hàng / ByClient / 5 (chúng tôi muốn xem thứ tự cho khách hàng cụ thể):
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);
}
}
Đây là giao diện cho dịch vụ đặt hàng:
public interface IOrderService
{
OrdersByClientViewModel GetByClient(int id);
}
Giao diện này trả về mô hình xem:
public class OrdersByClientViewModel
{
CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
IEnumerable<OrderViewModel> Orders { get; set; }
}
Đây là giao diện thực hiện. Nó sử dụng các lớp mô hình và kho lưu trữ để tạo mô hình khung nhìn:
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<>
trong thư viện IOC của bạn. Câu trả lời này rất cũ. Tôi nghĩ rằng giải pháp tốt nhất là kết hợp tất cả các kho lưu trữ trong một lớp được gọi UnitOfWork
. Nó nên chứa kho lưu trữ của mọi loại và một phương thức được gọi SaveChanges
. Tất cả các kho lưu trữ nên chia sẻ một bối cảnh EF.
Như Carnotaurus cho biết kho lưu trữ chịu trách nhiệm ánh xạ dữ liệu của bạn từ định dạng lưu trữ đến các đối tượng kinh doanh của bạn. Nó sẽ xử lý cả cách đọc và ghi dữ liệu (xóa, cập nhật quá) từ và vào bộ lưu trữ.
Mặt khác, mục đích của lớp dịch vụ là gói gọn logic kinh doanh vào một nơi duy nhất để thúc đẩy việc tái sử dụng mã và phân tách các mối quan tâm. Điều này thường có ý nghĩa gì đối với tôi trong thực tế khi xây dựng các trang web Asp.net MVC là tôi có cấu trúc này
[Trình điều khiển] gọi [Dịch vụ] người gọi [kho (ies)]
Một nguyên tắc tôi thấy hữu ích là giữ logic ở mức tối thiểu trong bộ điều khiển và kho lưu trữ.
Trong bộ điều khiển là bởi vì nó giúp giữ cho tôi KHÔ. Điều rất phổ biến là tôi cần sử dụng cùng một bộ lọc hoặc logic ở một nơi khác và nếu tôi đặt nó trong bộ điều khiển, tôi không thể sử dụng lại nó.
Trong kho lưu trữ là vì tôi muốn có thể thay thế bộ lưu trữ của mình (hoặc ORM) khi có thứ gì đó tốt hơn xuất hiện. Và nếu tôi có logic trong kho lưu trữ, tôi cần viết lại logic này khi tôi thay đổi kho lưu trữ. Nếu kho lưu trữ của tôi chỉ trả về IQueryable và mặt khác dịch vụ lọc, tôi sẽ chỉ cần thay thế ánh xạ.
Ví dụ, gần đây tôi đã thay thế một số kho lưu trữ Linq-To-Sql của tôi bằng EF4 và những kho lưu trữ theo nguyên tắc này có thể thay thế trong vài phút. Thay vào đó tôi có một số logic, đó chỉ là vấn đề hàng giờ.
onBeforeBuildBrowseQuery
và có thể sử dụng trình tạo truy vấn để thay đổi truy vấn.
Câu trả lời được chấp nhận (và được nâng cao hàng trăm lần) có một lỗ hổng lớn. Tôi muốn chỉ ra điều này trong bình luận nhưng nó sẽ chỉ bị chôn vùi ở đó trong 30 bình luận gì đó để chỉ ra ở đây.
Tôi đã tiếp nhận một ứng dụng doanh nghiệp được xây dựng theo cách đó và phản ứng ban đầu của tôi là WTH ? ViewModels trong lớp dịch vụ? Tôi không muốn thay đổi quy ước vì nhiều năm phát triển đã đi vào nó nên tôi tiếp tục trả lại ViewModels. Cậu bé đã biến thành cơn ác mộng khi chúng tôi bắt đầu sử dụng WPF. Chúng tôi (nhóm các nhà phát triển) luôn nói: ViewModel nào? Cái thực sự (cái chúng tôi đã viết cho WPF) hay cái dịch vụ? Chúng được viết cho một ứng dụng web và thậm chí có cờ IsReadOnly để tắt chỉnh sửa trong UI. Lỗi lớn, lỗ hổng lớn và tất cả chỉ vì một từ: ViewModel !!
Trước khi bạn mắc lỗi tương tự, đây là một số lý do khác ngoài câu chuyện của tôi ở trên:
Trả lại một ViewModel từ lớp dịch vụ là một điều không lớn. Điều đó giống như nói:
Nếu bạn muốn sử dụng các dịch vụ này, tốt hơn bạn nên sử dụng MVVM và đây là ViewModel bạn cần sử dụng. Ôi!
Các dịch vụ đang đưa ra giả định rằng chúng sẽ được hiển thị trong UI ở đâu đó. Điều gì nếu nó được sử dụng bởi một ứng dụng không phải UI như dịch vụ web hoặc dịch vụ windows?
Đó thậm chí không phải là một ViewModel thực sự. Một ViewModel thực sự có khả năng quan sát, các lệnh, v.v ... Đó chỉ là một POCO có tên xấu. (Xem câu chuyện của tôi ở trên để biết tại sao tên quan trọng.)
Ứng dụng tiêu thụ tốt hơn là một lớp trình bày (ViewModels được sử dụng bởi lớp này) và nó hiểu rõ hơn về C #. Một Ouch khác!
Làm ơn đừng làm vậy!
Thông thường, một kho lưu trữ được sử dụng làm giàn giáo để đưa vào các thực thể của bạn - một lớp dịch vụ sẽ ra ngoài và cung cấp một yêu cầu. Có khả năng bạn sẽ đặt một kho lưu trữ dưới lớp dịch vụ của bạn.
Lớp lưu trữ được triển khai để truy cập cơ sở dữ liệu và giúp mở rộng các hoạt động CRUD trên cơ sở dữ liệu. Trong khi đó một lớp dịch vụ bao gồm logic nghiệp vụ của ứng dụng và có thể sử dụng lớp kho lưu trữ để triển khai logic nhất định liên quan đến cơ sở dữ liệu. Trong một ứng dụng, tốt hơn là có một lớp kho lưu trữ và lớp dịch vụ riêng biệt. Việc có các lớp dịch vụ và kho lưu trữ riêng biệt làm cho mã trở nên mô đun hơn và tách rời cơ sở dữ liệu khỏi logic nghiệp vụ.