Trong một cuộc phỏng vấn xin việc, tôi đã được yêu cầu giải thích tại sao mẫu kho lưu trữ không phải là mẫu tốt để làm việc với các ORM như Entity Framework. Tại sao điều này là trường hợp?
Trong một cuộc phỏng vấn xin việc, tôi đã được yêu cầu giải thích tại sao mẫu kho lưu trữ không phải là mẫu tốt để làm việc với các ORM như Entity Framework. Tại sao điều này là trường hợp?
Câu trả lời:
Tôi không thấy bất kỳ lý do nào để mẫu Kho lưu trữ không hoạt động với Entity Framework. Mẫu lưu trữ là lớp trừu tượng bạn đặt trên lớp truy cập dữ liệu của mình. Lớp truy cập dữ liệu của bạn có thể là bất cứ thứ gì từ các thủ tục lưu sẵn ADO.NET thuần túy đến Entity Framework hoặc tệp XML.
Trong các hệ thống lớn, nơi bạn có dữ liệu đến từ các nguồn khác nhau (cơ sở dữ liệu / XML / dịch vụ web), thật tốt khi có một lớp trừu tượng. Mẫu Kho lưu trữ hoạt động tốt trong kịch bản này. Tôi không tin rằng Entity Framework đủ trừu tượng để che giấu những gì diễn ra sau hậu trường.
Tôi đã sử dụng mẫu Kho lưu trữ với Entity Framework làm phương thức lớp truy cập dữ liệu của mình và vẫn chưa gặp phải vấn đề gì.
Một ưu điểm khác của việc trừu tượng hóa DbContext
với Kho lưu trữ là khả năng kiểm tra đơn vị . Bạn có thể có IRepository
giao diện của mình có 2 triển khai, một (Kho lưu trữ thực) sử dụng DbContext
để nói chuyện với cơ sở dữ liệu và giao diện thứ hai, FakeRepository
có thể trả về các đối tượng trong bộ nhớ / dữ liệu giả. Điều này làm cho IRepository
đơn vị của bạn có thể kiểm tra được, do đó các phần khác của mã sử dụng IRepository
.
public interface IRepository
{
IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
private YourDbContext db;
private EFRepository()
{
db = new YourDbContext();
}
public IEnumerable<CustomerDto> GetCustomers()
{
return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
}
}
public MockRepository : IRepository
{
public IEnumerable<CustomerDto> GetCustomers()
{
// to do : return a mock list of Customers
// Or you may even use a mocking framework like Moq
}
}
Bây giờ sử dụng DI, bạn có được việc thực hiện
public class SomeService
{
IRepository repo;
public SomeService(IRepository repo)
{
this.repo = repo;
}
public void SomeMethod()
{
//use this.repo as needed
}
}
Lý do tốt nhất duy nhất để không sử dụng mẫu kho lưu trữ với Entity Framework? Entity Framework đã thực hiện một mẫu kho lưu trữ. DbContext
là UoW (Đơn vị công việc) của bạn và mỗi DbSet
là kho lưu trữ. Việc thực hiện một lớp khác trên đầu trang này không chỉ dư thừa mà còn khiến việc bảo trì khó khăn hơn.
Mọi người theo mô hình mà không nhận ra mục đích của mô hình. Trong trường hợp mẫu lưu trữ, mục đích là để trừu tượng hóa logic truy vấn cơ sở dữ liệu mức thấp. Trong những ngày xưa khi thực sự viết các câu lệnh SQL trong mã của bạn, mẫu kho lưu trữ là một cách để di chuyển SQL đó ra khỏi các phương thức riêng lẻ nằm rải rác trong cơ sở mã của bạn và bản địa hóa nó ở một nơi. Có một ORM như Entity Framework, NHibernate, v.v. là sự thay thế cho sự trừu tượng mã này và do đó, phủ nhận sự cần thiết của mẫu.
Tuy nhiên, đó không phải là một ý tưởng tồi để tạo ra một sự trừu tượng trên ORM của bạn, không có gì phức tạp như UoW / repostective. Tôi sẽ đi với một mẫu dịch vụ, nơi bạn xây dựng một API mà ứng dụng của bạn có thể sử dụng mà không cần biết hoặc quan tâm liệu dữ liệu đến từ Entity Framework, NHibernate hay API Web. Điều này đơn giản hơn nhiều, vì bạn chỉ cần thêm các phương thức vào lớp dịch vụ của mình để trả về dữ liệu mà ứng dụng của bạn cần. Ví dụ: nếu bạn đang viết ứng dụng Công việc, bạn có thể có một cuộc gọi dịch vụ để trả lại các mục đến hạn trong tuần này và chưa được hoàn thành. Tất cả các ứng dụng của bạn biết là nếu nó muốn thông tin này, nó gọi phương thức đó. Bên trong phương thức đó và trong dịch vụ của bạn nói chung, bạn tương tác với Entity Framework hoặc bất cứ điều gì khác mà bạn đang sử dụng. Sau đó, nếu sau đó bạn quyết định chuyển ORM hoặc lấy thông tin từ API Web,
Nghe có vẻ như đó là một đối số tiềm năng khi sử dụng mẫu kho lưu trữ, nhưng điểm khác biệt chính ở đây là dịch vụ là lớp mỏng hơn và hướng tới việc trả lại dữ liệu được nướng hoàn toàn, thay vì một thứ mà bạn tiếp tục truy vấn, như với kho.
DbContext
trong EF6 + (xem: msdn.microsoft.com/en-us/data/dn314429.aspx ). Ngay cả trong các phiên bản ít hơn, bạn có thể sử dụng một DbContext
lớp giống như giả với các socked DbSet
, vì DbSet
thực hiện một iterface , IDbSet
.
Đây là một từ Ayende Rahien: Kiến trúc trong hố của sự diệt vong: Các tệ nạn của lớp trừu tượng kho lưu trữ
Tôi không chắc liệu tôi có đồng ý với kết luận của anh ấy không. Đó là một cái bẫy 22 - một mặt, nếu tôi bọc Bối cảnh EF của mình trong các kho lưu trữ cụ thể theo loại với các phương thức truy xuất dữ liệu dành riêng cho truy vấn, tôi thực sự có thể kiểm tra mã của mình (loại), gần như không thể với Thực thể Khung một mình. Mặt khác, tôi mất khả năng truy vấn phong phú và duy trì các mối quan hệ ngữ nghĩa (nhưng ngay cả khi tôi có quyền truy cập đầy đủ vào các tính năng đó, tôi luôn cảm thấy như đang đi trên vỏ trứng xung quanh EF hoặc bất kỳ ORM nào khác tôi có thể chọn , vì tôi không bao giờ biết phương thức triển khai IQueryable nào có thể hỗ trợ hoặc có thể hỗ trợ, liệu nó sẽ diễn giải việc tôi thêm vào bộ sưu tập thuộc tính điều hướng như một sáng tạo hay chỉ là một liên kết, cho dù nó sẽ tải lười biếng hay háo hức hay không tải mặc định, v.v. Vì vậy, có lẽ điều này là tốt hơn. "Ánh xạ" đối tượng không trở kháng là một thứ gì đó của sinh vật thần thoại - có lẽ đó là lý do tại sao phiên bản Entity Framework mới nhất có tên mã là "Magic Unicorn").
Tuy nhiên, truy xuất các thực thể của bạn thông qua các phương thức truy xuất dữ liệu dành riêng cho truy vấn có nghĩa là các kiểm tra đơn vị của bạn hiện tại về cơ bản là các kiểm tra hộp trắng và bạn không có lựa chọn nào trong vấn đề này, vì bạn phải biết trước chính xác phương thức lưu trữ mà đơn vị đang kiểm tra sẽ thực hiện gọi để chế nhạo nó. Và bạn vẫn chưa thực sự tự kiểm tra các truy vấn, trừ khi bạn cũng viết các bài kiểm tra tích hợp.
Đây là những vấn đề phức tạp cần một giải pháp phức tạp. Bạn không thể sửa nó bằng cách giả vờ rằng tất cả các thực thể của bạn là các loại riêng biệt không có mối quan hệ giữa chúng và nguyên tử chúng vào kho lưu trữ của riêng chúng. Vâng, bạn có thể , nhưng nó hút.
Cập nhật: Tôi đã có một số thành công khi sử dụng nhà cung cấp Effort cho Entity Framework. Effort là một nhà cung cấp trong bộ nhớ (nguồn mở) cho phép bạn sử dụng EF trong các thử nghiệm chính xác theo cách bạn sẽ sử dụng nó đối với cơ sở dữ liệu thực. Tôi đang xem xét chuyển đổi tất cả các thử nghiệm trong dự án này Tôi đang làm việc để sử dụng nhà cung cấp này, vì nó dường như làm cho mọi thứ dễ dàng hơn nhiều. Đó là giải pháp duy nhất tôi tìm thấy cho đến nay giải quyết tất cả các vấn đề mà tôi đã nói về trước đó. Chỉ có điều là có một chút chậm trễ khi bắt đầu các thử nghiệm của tôi khi nó tạo cơ sở dữ liệu trong bộ nhớ (nó sử dụng một gói khác gọi là NMemory để làm điều này), nhưng tôi không xem đây là một vấn đề thực sự. Có một bài viết về Dự án mã nói về việc sử dụng Effort (so với SQL CE) để thử nghiệm.
DbContext
. Bất kể, bạn luôn có thể chế giễu DbSet
, và dù sao đó cũng là thịt của Entity Framework. DbContext
ít hơn một lớp để chứa các DbSet
thuộc tính của bạn (kho lưu trữ) trong một vị trí (đơn vị công việc), đặc biệt là trong bối cảnh thử nghiệm đơn vị, trong đó tất cả các công cụ khởi tạo và kết nối cơ sở dữ liệu không muốn hoặc không cần thiết.
Lý do tại sao bạn có thể sẽ làm điều đó là vì nó hơi dư thừa. Entity Framework mang đến cho bạn rất nhiều lợi thế về mã hóa và chức năng, đó là lý do tại sao bạn sử dụng nó, nếu sau đó bạn lấy nó và bọc nó trong một mẫu kho lưu trữ mà bạn đang loại bỏ những lợi thế đó, bạn cũng có thể sử dụng bất kỳ lớp truy cập dữ liệu nào khác.
Về lý thuyết tôi nghĩ sẽ hợp lý khi đóng gói logic kết nối cơ sở dữ liệu để làm cho nó dễ sử dụng lại hơn, nhưng như liên kết dưới đây lập luận, các khung hiện đại của chúng ta về cơ bản hiện đang xử lý vấn đề này.
ISessionFactory
và ISession
rất dễ bị chế giễu), thật không dễ dàng gì DbContext
, thật không may ...
Một lý do rất tốt để sử dụng mẫu kho lưu trữ là cho phép tách logic nghiệp vụ của bạn và / hoặc giao diện người dùng của bạn khỏi System.Data.Entity. Có rất nhiều lợi thế cho việc này, bao gồm cả lợi ích thực sự trong thử nghiệm đơn vị bằng cách cho phép anh ta sử dụng Fakes hoặc Mocks.
Chúng tôi đã gặp sự cố với các phiên bản DbContext của Entity Framework trùng lặp nhưng khác nhau khi một bộ chứa IoC chứa các kho lưu trữ mới () cho mỗi loại (ví dụ: UserRep repository và GroupRep repository mà mỗi cuộc gọi IDbset riêng của chúng từ DBContext), đôi khi có thể gây ra nhiều bối cảnh cho mỗi yêu cầu (trong một bối cảnh MVC / web).
Hầu hết thời gian nó vẫn hoạt động, nhưng khi bạn thêm một lớp dịch vụ lên trên đó và các dịch vụ đó giả sử các đối tượng được tạo với một bối cảnh sẽ được gắn chính xác như các bộ sưu tập con vào một đối tượng mới trong một ngữ cảnh khác, đôi khi nó không thành công và đôi khi không ' t tùy thuộc vào tốc độ của các cam kết.
Sau khi thử mô hình kho lưu trữ trên dự án nhỏ, tôi khuyên không nên sử dụng nó; không phải vì nó làm phức tạp hệ thống của bạn, và không phải vì dữ liệu chế nhạo là ác mộng, mà bởi vì thử nghiệm của bạn trở nên vô dụng !!
Dữ liệu giả định cho phép bạn thêm chi tiết mà không cần tiêu đề, thêm bản ghi vi phạm các ràng buộc cơ sở dữ liệu và xóa các thực thể mà cơ sở dữ liệu sẽ từ chối xóa. Trong thế giới thực, một bản cập nhật có thể ảnh hưởng đến nhiều bảng, nhật ký, lịch sử, tóm tắt, v.v., cũng như các cột như trường ngày sửa đổi lần cuối, khóa được tạo tự động, trường được tính toán.
Trong ngắn hạn, thử nghiệm của bạn trên cơ sở dữ liệu thực cho bạn kết quả thực và bạn có thể kiểm tra không chỉ các dịch vụ và giao diện mà còn cả hành vi của cơ sở dữ liệu. Bạn có thể kiểm tra xem các quy trình được lưu trữ của bạn có thực hiện đúng dữ liệu hay không, trả về kết quả mong đợi hoặc bản ghi bạn đã gửi để xóa thực sự bị xóa! Các thử nghiệm như vậy cũng có thể phơi bày các vấn đề như quên đưa ra lỗi từ thủ tục được lưu trữ và hàng ngàn tình huống như vậy.
Tôi nghĩ khung thực thể triển khai mô hình kho lưu trữ tốt hơn bất kỳ bài viết nào tôi đã đọc cho đến nay và nó vượt xa những gì họ đang cố gắng thực hiện.
Kho lưu trữ là cách tốt nhất vào những ngày chúng tôi sử dụng XBase, AdoX và Ado.Net, nhưng với thực thể !! (Kho lưu trữ trên kho)
Cuối cùng tôi nghĩ rằng có quá nhiều người đầu tư nhiều thời gian vào việc học và triển khai mô hình kho lưu trữ và họ từ chối để nó đi. Chủ yếu là để chứng minh với bản thân rằng họ đã không lãng phí thời gian của họ.
Nguyên nhân là do Di chuyển: Không thể để di chuyển hoạt động, vì chuỗi kết nối nằm trong web.config. Nhưng, DbContext nằm trong lớp Kho lưu trữ. IDbContextFactory cần phải có chuỗi cấu hình vào cơ sở dữ liệu. Nhưng không có cách nào di chuyển có được chuỗi kết nối từ web.config.
Có nhiều công việc xung quanh nhưng tôi chưa tìm được giải pháp sạch cho việc này!