Có một lợi thế thực sự để lưu trữ chung?


28

Đã đọc qua một số bài viết về những lợi ích của việc tạo Kho lưu trữ chung cho một ứng dụng mới ( ví dụ ). Ý tưởng có vẻ hay vì nó cho phép tôi sử dụng cùng một kho lưu trữ để thực hiện một số việc cho một số loại thực thể khác nhau cùng một lúc:

IRepository repo = new EfRepository(); // Would normally pass through IOC into constructor 
var c1 = new Country() { Name = "United States", CountryCode = "US" };
var c2 = new Country() { Name = "Canada", CountryCode = "CA" };
var c3 = new Country() { Name = "Mexico", CountryCode = "MX" };
var p1 = new Province() { Country = c1, Name = "Alabama", Abbreviation = "AL" };
var p2 = new Province() { Country = c1, Name = "Alaska", Abbreviation = "AK" };
var p3 = new Province() { Country = c2, Name = "Alberta", Abbreviation = "AB" };
repo.Add<Country>(c1);
repo.Add<Country>(c2);
repo.Add<Country>(c3);
repo.Add<Province>(p1);
repo.Add<Province>(p2);
repo.Add<Province>(p3);
repo.Save();

Tuy nhiên, phần còn lại của việc triển khai Kho lưu trữ có sự phụ thuộc lớn vào Linq:

IQueryable<T> Query();
IList<T> Find(Expression<Func<T,bool>> predicate);
T Get(Expression<Func<T,bool>> predicate);
T First(Expression<Func<T,bool>> predicate);
//... and so on

Mẫu kho lưu trữ này hoạt động tuyệt vời cho Entity Framework và khá nhiều cung cấp ánh xạ 1 đến 1 của các phương thức có sẵn trên DbContext / DbSet. Nhưng với sự hấp thụ chậm của Linq trên các công nghệ truy cập dữ liệu khác ngoài Entity Framework, điều này mang lại lợi thế gì cho việc làm việc trực tiếp với DbContext?

Tôi đã cố gắng viết phiên bản PetaPoco của Kho lưu trữ, nhưng PetaPoco không hỗ trợ Linq Expressions, việc tạo giao diện IRep repository chung khá vô dụng trừ khi bạn chỉ sử dụng nó cho GetAll, GetById, Thêm, Cập nhật, Xóa và Lưu phương thức và sử dụng nó như là một lớp cơ sở. Sau đó, bạn phải tạo các kho lưu trữ cụ thể với các phương thức chuyên dụng để xử lý tất cả các mệnh đề "ở đâu" mà trước đây tôi có thể chuyển qua làm vị ngữ.

Mẫu Kho lưu trữ chung có hữu ích cho mọi thứ bên ngoài Entity Framework không? Nếu không, tại sao ai đó sẽ sử dụng nó thay vì làm việc trực tiếp với Entity Framework?


Liên kết gốc không phản ánh mẫu tôi đang sử dụng trong mã mẫu của mình. Đây là một ( liên kết cập nhật ).



1
Là câu hỏi thực sự về "advatange of generic repository" hay là "làm thế nào để ẩn các truy vấn phức tạp đằng sau một giao diện kho lưu trữ chung"? Có thể là chung chung nếu giao diện và cách sử dụng của nó phụ thuộc vào linq? Kho lưu trữ của chúng tôi có phương thức QueryByExample hoàn toàn độc lập với kỹ thuật tìm kiếm và sẽ cho phép thay đổi triển khai.
k3b

"Nó cho phép tôi sử dụng cùng một kho lưu trữ để thực hiện một số điều cho một số loại thực thể khác nhau" => Có phải tôi hoặc bạn đã hiểu nhầm kho lưu trữ chung là gì (bao gồm cả liên quan đến bài viết được đề cập)? Đối với tôi, kho lưu trữ chung luôn có nghĩa là tạo khuôn mẫu cho tất cả các repos với một lớp hoặc giao diện duy nhất , không có một cá thể kho lưu trữ duy nhất quản lý sự tồn tại của tất cả các thực thể bất kể loại nào ...
guillaume31

Câu trả lời:


35

Kho lưu trữ chung thậm chí còn vô dụng (và IMHO cũng tệ) cho Entity Framework. Nó không mang lại bất kỳ giá trị bổ sung nào cho những gì đã được cung cấp bởi IDbSet<T>(đó là kho lưu trữ chung của btw.).

Vì bạn đã tìm thấy đối số rằng kho lưu trữ chung có thể được thay thế bằng cách triển khai cho công nghệ truy cập dữ liệu khác khá yếu vì nó có thể yêu cầu viết nhà cung cấp Linq của riêng bạn.

Đối số phổ biến thứ hai về kiểm thử đơn vị đơn giản cũng sai vì việc lưu trữ / thiết lập kho lưu trữ dữ liệu trong bộ nhớ thay thế nhà cung cấp Linq bằng một nhà cung cấp khác có các khả năng khác nhau. Nhà cung cấp Linq-to-entity chỉ hỗ trợ tập hợp con các tính năng Linq - thậm chí nó không hỗ trợ tất cả các phương thức có sẵn trên IQueryable<T>giao diện. Chia sẻ cây biểu thức giữa lớp truy cập dữ liệu và lớp logic nghiệp vụ ngăn chặn mọi giả mạo của lớp truy cập dữ liệu - logic truy vấn phải được tách riêng.

Nếu bạn muốn có sự trừu tượng hóa "chung chung" mạnh mẽ, bạn cũng phải liên quan đến các mẫu khác. Trong trường hợp này, bạn cần sử dụng ngôn ngữ truy vấn trừu tượng có thể được dịch bởi kho lưu trữ sang ngôn ngữ truy vấn cụ thể được hỗ trợ bởi lớp truy cập dữ liệu đã sử dụng. Điều này được xử lý bởi mẫu Đặc điểm kỹ thuật. Linq on IQueryablelà đặc tả (nhưng bản dịch yêu cầu nhà cung cấp - hoặc một số khách truy cập tùy chỉnh dịch cây biểu thức thành truy vấn) nhưng bạn có thể xác định phiên bản đơn giản của riêng mình và sử dụng nó. Ví dụ NHibernate sử dụng API Tiêu chí. Cách đơn giản nhất là sử dụng kho lưu trữ cụ thể với các phương thức cụ thể. Cách này là cách đơn giản nhất để thực hiện, đơn giản nhất để kiểm tra và đơn giản nhất để giả mạo trong các bài kiểm tra đơn vị vì logic truy vấn bị ẩn hoàn toàn và tách biệt đằng sau sự trừu tượng.


Chỉ cần một câu hỏi nhanh, NHibernate có ISessionthể dễ dàng bị chế giễu cho các mục đích thử nghiệm đơn vị chung và tôi sẵn sàng bỏ 'kho' khi làm việc với EF - nhưng có cách nào dễ dàng hơn để tái tạo điều đó không? Một cái gì đó tương tự ISessionISessionFactory, IDbContextbởi vì tôi không thể nói xa được ...
Patryk wiek

Không có không IDbContext- nếu bạn muốn, IDbContextbạn chỉ cần tạo một cái và thực hiện nó trên bối cảnh xuất phát của bạn.
Ladislav Mrnka

Vì vậy, bạn đang nói rằng đối với các ứng dụng MVC hàng ngày, IDbset <T> nên cung cấp một kho lưu trữ chung đủ tốt để quên hoàn toàn mẫu kho lưu trữ. Ngoại trừ các tình huống đặc biệt, ví dụ như bạn không cho phép bất kỳ tài liệu tham khảo DAL nào trong dự án MVC của mình.
ProfK

1
Câu trả lời tốt. Một số nhà phát triển chỉ tạo ra một lớp trừu tượng khác mà không có lý do. IMO, EF không cần kho lưu trữ chung cũng như đơn vị công việc (chúng được tích hợp sẵn)
ashraf

1
IDbSet <T> là một kho lưu trữ chung có sự phụ thuộc vào Khung thực thể, loại này đánh bại mục đích sử dụng kho lưu trữ chung để loại bỏ sự phụ thuộc vào Khung thực thể.
Joel McBeth

7

Vấn đề không phải là mô hình kho lưu trữ. Có một sự trừu tượng giữa việc lấy dữ liệu và cách bạn nhận được nó là một điều tốt.

Vấn đề ở đây là việc thực hiện. Giả sử rằng một biểu thức tùy ý sẽ hoạt động để lọc là tốt nhất.

Làm cho một kho lưu trữ làm việc cho tất cả các đối tượng của bạn trực tiếp sẽ bỏ lỡ điểm. Các đối tượng dữ liệu sẽ hiếm khi được ánh xạ trực tiếp đến các đối tượng kinh doanh. Truyền vào T để lọc làm cho rất ít ý nghĩa trong những tình huống này. Và việc cung cấp nhiều chức năng đó đảm bảo rằng bạn không thể hỗ trợ tất cả các chức năng đó một khi nhà cung cấp khác xuất hiện.


Vì vậy, sẽ có ý nghĩa hơn khi có một Kho lưu trữ cho mỗi đối tượng dữ liệu hoặc nhóm các đối tượng liên quan chặt chẽ với các phương thức cụ thể như GetById (int id), SortedList (), v.v? Tôi có trả về danh sách các đối tượng dữ liệu từ kho lưu trữ hay chuyển đổi chúng thành đối tượng kinh doanh với các trường cần thiết không? Kinda nghĩ rằng đó là những gì đã xảy ra trong lớp Dịch vụ / Kinh doanh.
Sam

1
@sam - Tôi có xu hướng ủng hộ các kho lưu trữ cụ thể hơn, vâng. Nếu họ đang dịch hay không phụ thuộc vào nơi mọi thứ có thể thay đổi. Nếu tên miền của bạn được xác định rõ, tôi sẽ trả lại mọi thứ theo hình dạng của tên miền. Nếu dữ liệu được xác định rõ, tôi sẽ trả lại mọi thứ theo hình dạng cấu trúc dữ liệu. Nếu không, tôi có một thực thể đóng vai trò là cơ sở được xác định rõ để xây dựng và sau đó thích nghi với / từ đó.
Telastyn

1
Không có lý do gì bạn không thể có kho lưu trữ cụ thể ủy thác các nội dung phổ biến cho kho lưu trữ chung, nhưng cũng có các phương thức dành riêng cho kho lưu trữ bổ sung cho các cuộc gọi phức tạp hơn. Tôi đã thấy nó được thực hiện theo cách đó nhiều lần.
Eric King

@EricKing Tôi cũng vậy, và nó cũng tốt ở đó, nhưng nó có xu hướng rò rỉ một số trừu tượng vì những thứ phổ biến có xu hướng chỉ tồn tại do tính phổ biến của cách lưu trữ dữ liệu (ví dụ GetByID yêu cầu các bảng có ID).
Telastyn

@Telastyn Vâng, tôi cũng đồng ý với điều đó, nhưng nó cũng xảy ra với các kho lưu trữ cụ thể. Trừu tượng rò rỉ không cụ thể cho các kho lưu trữ chung. (Wow, tôi có thể nói thêm rằng vụng về nữa không?)
Eric King

2

Giá trị của lớp dữ liệu chung (kho lưu trữ là một loại lớp dữ liệu cụ thể) đang cho phép mã thay đổi cơ chế lưu trữ bên dưới mà ít hoặc không ảnh hưởng đến mã gọi.

Về lý thuyết, điều này hoạt động tốt. Trong thực tế, như bạn đã quan sát, sự trừu tượng thường bị rò rỉ. Các cơ chế được sử dụng để truy cập dữ liệu trong một khác với các cơ chế trong một cơ chế khác. Trong một số trường hợp, cuối cùng bạn viết mã hai lần: một lần trong lớp nghiệp vụ và lặp lại nó trong lớp dữ liệu.

Cách hiệu quả nhất để tạo một lớp dữ liệu chung là biết các loại nguồn dữ liệu khác nhau mà ứng dụng sẽ sử dụng trước đó. Như bạn đã thấy, giả sử LINQ hoặc SQL là phổ quát có thể là một vấn đề. Cố gắng trang bị thêm các cửa hàng dữ liệu mới có thể sẽ dẫn đến việc viết lại.

[Chỉnh sửa: Đã thêm như sau.]

Nó cũng phụ thuộc vào những gì ứng dụng cần từ lớp dữ liệu. Nếu ứng dụng chỉ tải hoặc lưu trữ các đối tượng, lớp dữ liệu có thể rất đơn giản. Khi nhu cầu tìm kiếm, sắp xếp và bộ lọc tăng lên, độ phức tạp của lớp dữ liệu tăng lên và sự trừu tượng bắt đầu bị rò rỉ (chẳng hạn như phơi bày các truy vấn LINQ trong câu hỏi). Tuy nhiên, khi người dùng có thể cung cấp các truy vấn của riêng họ, chi phí / lợi ích của lớp dữ liệu cần phải được cân nhắc cẩn thận.


1

Có một lớp mã trên cơ sở dữ liệu là đáng giá trong hầu hết các trường hợp. Tôi thường thích một mẫu "GetByXXXXX" trong mã đã nói - nó cho phép bạn tối ưu hóa các truy vấn phía sau nó khi cần trong khi giữ cho giao diện người dùng không bị lộn xộn giao diện dữ liệu.

Tận dụng lợi thế của tướng chắc chắn là trò chơi công bằng - có một Load<T>(int id)phương pháp làm cho rất nhiều ý nghĩa. Nhưng việc xây dựng các kho lưu trữ xung quanh LINQ là những năm 2010 tương đương với việc bỏ các truy vấn sql ở mọi nơi với một chút an toàn loại được thêm vào.


0

Vâng, với liên kết được cung cấp, tôi có thể thấy rằng nó có thể là một trình bao bọc tiện dụng cho một DataServiceContext, nhưng không làm giảm các thao tác mã cũng như cải thiện khả năng đọc. Bên cạnh đó, quyền truy cập DataServiceQuery<T>bị cản trở, hạn chế tính linh hoạt .Where().Single(). Cũng không AddRange()hoặc thay thế được cung cấp. Cũng không được Delete(Predicate)cung cấp có thể hữu ích ( repo.Delete<Person>( p => p.Name=="Joe" );để xóa Joe-s). V.v.

Kết luận: một API như vậy cản trở API gốc và giới hạn nó trong một vài thao tác đơn giản.


Bạn nói đúng. Đó không phải là bài viết sử dụng mẫu tôi có trong mã mẫu của tôi. Sẽ cố gắng tìm liên kết khi tôi về nhà.
Sam

Liên kết cập nhật được thêm vào dưới cùng của câu hỏi.
Sam

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.