2009-08-28 9 views
20

Vì vậy, tôi đang thực hiện mô hình kho trong một ứng dụng và tình cờ gặp hai "vấn đề" trong sự hiểu biết của tôi về các mô hình:Pattern Repository Thực hành tốt nhất

  1. Truy vấn - Tôi đã đọc các phản hồi IQueryable không nên được sử dụng khi sử dụng kho. Tuy nhiên, rõ ràng là bạn muốn để bạn không trả về một danh sách đầy đủ các đối tượng mỗi khi bạn gọi một phương thức. Nó có nên được thực hiện không? Nếu tôi có một phương pháp IEnumerable được gọi là Danh sách, "thực hành tốt nhất" chung cho một IQueryable là gì? Những thông số nào nên/không nên có?

  2. Giá trị vô hướng - Cách tốt nhất (sử dụng mẫu Kho lưu trữ) là gì để trả về một giá trị vô hướng duy nhất mà không phải trả lại toàn bộ bản ghi? Từ một quan điểm hiệu suất, nó sẽ không hiệu quả hơn để trả về chỉ một giá trị vô hướng duy nhất trên toàn bộ một hàng?

Trả lời

30

Nói đúng, một Kho lưu trữ cung cấp ngữ nghĩa bộ sưu tập để nhận/đưa các đối tượng miền. Nó cung cấp một trừu tượng xung quanh việc thực hiện hiện thực hóa của bạn (ORM, được cuộn tay, mô phỏng) để người tiêu dùng của các đối tượng miền được tách riêng khỏi các chi tiết đó. Trong thực tế, một Repository thường tóm tắt quyền truy cập vào các thực thể, tức là các đối tượng miền có nhận dạng và thường là vòng đời liên tục (trong DDD, một Repository cung cấp quyền truy cập vào các Roots tổng hợp).

Một giao diện tối thiểu cho một kho lưu trữ như sau:

void Add(T entity); 
void Remove(T entity); 
T GetById(object id); 
IEnumerable<T> Find(Specification spec); 

Mặc dù bạn sẽ thấy sự khác biệt đặt tên và việc bổ sung Save/SaveOrUpdate ngữ nghĩa, phía trên là ý tưởng 'tinh khiết'. Bạn nhận được các thành viên Add/Remove ICollection cộng với một số người tìm kiếm.Nếu bạn không sử dụng IQueryable, bạn cũng sẽ thấy các phương thức tìm kiếm trên kho lưu trữ như:

FindCustomersHavingOrders(); 
FindCustomersHavingPremiumStatus(); 

Có hai vấn đề liên quan đến việc sử dụng IQueryable trong ngữ cảnh này. Thứ nhất là tiềm năng rò rỉ chi tiết triển khai cho khách hàng dưới dạng mối quan hệ của đối tượng tên miền, tức là vi phạm Luật Demeter. Thứ hai là kho lưu trữ tìm kiếm các trách nhiệm có thể không thuộc về kho lưu trữ đối tượng miền thích hợp, ví dụ: tìm các phép chiếu ít về đối tượng miền được yêu cầu hơn so với dữ liệu có liên quan.

Ngoài ra, bằng cách sử dụng IQueryable 'ngắt' mẫu: Kho lưu trữ với IQueryable có thể hoặc không thể cung cấp quyền truy cập vào 'đối tượng miền'. IQueryable cung cấp cho khách hàng rất nhiều tùy chọn về những gì sẽ được thực hiện khi truy vấn cuối cùng được thực thi. Đây là lực đẩy chính của cuộc tranh luận về việc sử dụng IQueryable.

Về giá trị vô hướng, bạn không nên sử dụng kho lưu trữ để trả về giá trị vô hướng. Nếu bạn cần một vô hướng, bạn thường sẽ nhận được điều này từ chính thực thể đó. Nếu điều này nghe có vẻ không hiệu quả, có nghĩa là, nhưng bạn có thể không nhận thấy, tùy thuộc vào các đặc tính/yêu cầu tải của bạn. Trong trường hợp bạn cần các chế độ xem thay thế của đối tượng miền, vì lý do hiệu suất hoặc vì bạn cần hợp nhất dữ liệu từ nhiều đối tượng miền, bạn có hai tùy chọn.

1) Sử dụng kho lưu trữ của đối tượng để tìm các thực thể và dự án/bản đồ được chỉ định cho chế độ xem được làm phẳng.

2) Tạo giao diện công cụ tìm dành riêng để trả về loại tên miền mới đóng gói chế độ xem phẳng mà bạn cần. Đây sẽ không phải là một Kho lưu trữ vì sẽ không có ngữ nghĩa Bộ sưu tập, nhưng nó có thể sử dụng các kho lưu trữ hiện có dưới các bìa.

Một điều cần xem xét nếu bạn sử dụng Kho lưu trữ 'thuần' để truy cập các thực thể tồn tại là bạn thỏa hiệp một số lợi ích của ORM. Trong một triển khai 'thuần', khách hàng không thể cung cấp ngữ cảnh cho cách đối tượng miền sẽ được sử dụng, vì vậy bạn không thể nói kho lưu trữ: 'hey, tôi sẽ thay đổi thuộc tính customer.Name, do đó, don 't bận tâm nhận được những tài liệu tham khảo háo hức nạp.' Mặt khác, câu hỏi đặt ra là liệu một khách hàng có nên biết về những thứ đó không. Đó là một thanh kiếm hai lưỡi. Theo như sử dụng IQueryable, hầu hết mọi người dường như cảm thấy thoải mái với việc 'phá vỡ' mẫu để có được lợi ích của thành phần truy vấn động, đặc biệt là đối với các trách nhiệm của khách hàng như phân trang/phân loại. Trong trường hợp này, bạn có thể có:

Add(T entity); 
Remove(T entity); 
T GetById(object id); 
IQueryable<T> Find(); 

và sau đó bạn có thể làm đi với tất cả các phương pháp đó Finder tùy chỉnh, mà thực sự lộn xộn Repository như yêu cầu truy vấn của bạn phát triển.

+1

Tôi đã xem bài đăng của jbogard về chủ đề này hôm nay: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/02/ddd-repository-implementation-patterns.aspx – pfries

+3

+1: ". .. hầu hết mọi người dường như cảm thấy thoải mái với việc 'phá vỡ' mẫu để có được lợi ích của thành phần truy vấn động ... " –

3

Về 1: Theo tôi có thể thấy, bản thân tôi không phải là vấn đề được trả lại từ kho lưu trữ. Điểm của một kho lưu trữ là nó sẽ trông giống như một đối tượng chứa tất cả dữ liệu của bạn. Vì vậy, bạn có thể yêu cầu kho lưu trữ dữ liệu. Nếu bạn có nhiều hơn một đối tượng cần cùng một dữ liệu, công việc của kho lưu trữ là lưu trữ dữ liệu, vì vậy hai ứng dụng khách của kho lưu trữ của bạn sẽ nhận được các cá thể giống nhau - vì vậy nếu một khách hàng thay đổi thuộc tính thì người kia sẽ thấy , bởi vì họ đang trỏ đến cùng một ví dụ.

Nếu kho lưu trữ thực sự là chính nhà cung cấp LINQ, thì điều đó có thể phù hợp. Nhưng hầu hết mọi người chỉ cho phép vượt qua IQuerable của nhà cung cấp LINQ-to-sql qua, có hiệu lực bỏ qua trách nhiệm của kho lưu trữ. Vì vậy, kho lưu trữ không phải là một kho lưu trữ ở tất cả, ít nhất là theo sự hiểu biết của tôi và cách sử dụng của mô hình.

Về 2: Đương nhiên, hiệu suất hiệu quả hơn chỉ trả về một giá trị duy nhất từ ​​cơ sở dữ liệu so với toàn bộ bản ghi. Nhưng bằng cách sử dụng một mẫu kho lưu trữ, bạn sẽ không trả về các bản ghi nào cả, bạn sẽ trả về các đối tượng nghiệp vụ. Vì vậy, logic ứng dụng không nên liên quan đến chính nó với các trường, nhưng với các đối tượng miền.

Nhưng hiệu quả hơn là trả lại một giá trị đơn lẻ so với đối tượng miền hoàn chỉnh? Có thể bạn sẽ không thể đo được sự khác biệt nếu lược đồ cơ sở dữ liệu của bạn được xác định hợp lý.

Điều quan trọng hơn là phải có mã sạch, dễ hiểu - thay vì tối ưu hóa hiệu suất vi mô lên phía trước.

7

Để trả lời @lordinateur Tôi không thực sự thích cách defacto để chỉ định giao diện kho lưu trữ.

Vì giao diện trong giải pháp của bạn yêu cầu mọi triển khai kho lưu trữ yêu cầu ít nhất là Thêm, Xóa, GetById, v.v. Bây giờ hãy xem xét một trường hợp không có ý nghĩa để Lưu thông qua một trường hợp cụ thể của một kho lưu trữ, bạn vẫn phải triển khai các phương thức còn lại với NotImplementedException hoặc một cái gì đó tương tự.

Tôi thích chia tờ khai giao diện kho lưu trữ của tôi như vậy:

interface ICanAdd<T> 
{ 
    T Add(T entity); 
} 

interface ICanRemove<T> 
{ 
    bool Remove(T entity); 
} 

interface ICanGetById<T> 
{ 
    T Get(int id); 
} 

Một triển khai kho đặc biệt đối với một tổ chức SomeClass có thể do đó trông giống như sau:

interface ISomeRepository 
    : ICanAdd<SomeClass>, 
     ICanRemove<SomeClass> 
{ 
    SomeClass Add(SomeClass entity); 
    bool Remove(SomeClass entity); 
} 

Hãy lùi lại một bước và hãy xem tại sao tôi nghĩ rằng đây là một thực hành tốt hơn so với thực hiện tất cả các phương pháp CRUD trong một giao diện chung chung.

Một số đối tượng có yêu cầu khác với các yêu cầu khác. Không thể xóa đối tượng của khách hàng, không thể cập nhật PurchaseOrder và đối tượng Mua sắm chỉ có thể được tạo . Khi một người đang sử dụng giao diện IRO của chung này rõ ràng là gây ra sự cố trong việc triển khai .

Những thực hiện chống mẫu thường sẽ thực hiện giao diện đầy đủ của họ sau đó sẽ ném ngoại lệ cho các phương pháp mà họ không hỗ trợ. Bên cạnh không đồng ý với nhiều nguyên tắc OO này phá vỡ hy vọng của họ về khả năng sử dụng IRepository trừu tượng của họ một cách hiệu quả trừ khi họ cũng bắt đầu đưa các phương pháp trên đó cho các đối tượng hay không cho được hỗ trợ và tiếp tục thực hiện họ.

Một phương pháp phổ biến để vấn đề này là phải di chuyển đến nhiều giao diện dạng hạt như ICanDelete, ICanUpdate, ICanCreate vv vv Điều này trong khi làm việc xung quanh nhiều vấn đề đã nổi lên về OO nguyên tắc cũng làm giảm đáng kể việc sử dụng lại mã số đang được sử dụng vì hầu hết thời gian sẽ không có thể sử dụng trường hợp cụ thể của Kho lưu trữ nữa.

Không ai trong số chúng tôi thích viết cùng mã hơn và hơn. Tuy nhiên, một hợp đồng kho là một đường nối kiến ​​trúc là địa điểm sai để mở rộng hợp đồng để làm cho hợp đồng chung chung hơn.

Các đoạn trích này được lấy từ các trang this post nơi bạn cũng có thể đọc thêm thảo luận trong nhận xét.

+0

Kho lưu trữ mà bạn không 'Lưu' thông qua giao diện như thế nào? Nếu bạn thả phần Add/Remove, bạn chỉ cần có một giao diện Finder. Nếu tất cả những gì bạn muốn làm là tìm nội dung, bạn không cần một kho lưu trữ mà là một số loại triển khai ICustomerFinder.Điều đó nói rằng, Repository của bạn có thể thực hiện một Finder: giao diện ICustomerRepository: IRepository , ICustomerFinder Chỉ cần làm rõ, bạn không thực sự 'Lưu' thông qua giao diện kho lưu trữ, đó sẽ là trách nhiệm của một số loại đơn vị công việc thực hiện. Một phiên như vậy được quản lý tốt nhất bên ngoài Kho lưu trữ. – pfries

+0

Điểm của Greg về thành phần là một điểm tốt, tức là bạn có thể triển khai kho lưu trữ đối tượng miền với một kho lưu trữ chung bên trong. Đó là cách nó hoạt động trong thực tế khi sử dụng một giao diện như ICustomerRepository xung quanh một ORM như NHibernate (mà thay thế cho một kho lưu trữ chung). Điểm chính của anh ta là không sử dụng các hợp đồng chung chung ở các đường nối của ứng dụng của bạn. Điều đó khá hợp lý; nếu hợp đồng của bạn là ICustomerRepository và không phải là IRepository, bạn tránh được các vấn đề mà anh ta mô tả. Tôi không yêu tất cả những người tìm kiếm mặc dù họ thể hiện ý định. Điều gì về các đối tượng truy vấn? – pfries

+0

froghammr: sau tất cả các nghiên cứu của tôi, tôi đang giải quyết với các đối tượng truy vấn. Tôi đang phát triển một ứng dụng di động trong MonoDroid/C# và không có LINQ, Entity hoặc Hibernate, vì vậy tôi phải viết mọi thứ từ đầu. Mặc dù vậy, chi phí của một Finder/đặc điểm kỹ thuật là sâu sắc. – samosaris