Tạo một lớp trừu tượng trên lớp ORM


12

Tôi tin rằng nếu bạn có kho lưu trữ của mình, hãy sử dụng ORM mà nó đã đủ trừu tượng hóa từ cơ sở dữ liệu.

Tuy nhiên, nơi tôi đang làm việc bây giờ, ai đó tin rằng chúng ta nên có một lớp trừu tượng ORM trong trường hợp chúng ta muốn thay đổi ORM sau này.

Có thực sự cần thiết hay đơn giản chỉ là quá nhiều để tạo ra một lớp sẽ hoạt động trên nhiều ORM?

Biên tập

Chỉ để cung cấp thêm chi tiết:

  1. Chúng tôi có lớp POCO và Lớp thực thể được ánh xạ với AutoMapper. Lớp thực thể được sử dụng bởi lớp Kho lưu trữ. Lớp kho lưu trữ sau đó sử dụng lớp trừu tượng bổ sung để giao tiếp với Entity Framework.
  2. Lớp nghiệp vụ không có cách nào truy cập trực tiếp vào Entity Framework. Ngay cả khi không có lớp trừu tượng bổ sung trên ORM, lớp này vẫn cần sử dụng lớp dịch vụ sử dụng lớp kho lưu trữ. Trong cả hai trường hợp, lớp nghiệp vụ hoàn toàn tách biệt với ORM.
  3. Đối số chính là có thể thay đổi ORM trong tương lai. Vì nó thực sự được bản địa hóa bên trong lớp Kho lưu trữ, nên đối với tôi, nó đã được phân tách rõ ràng và tôi không hiểu tại sao một lớp trừu tượng bổ sung được yêu cầu phải có mã "chất lượng".

3
lớp bổ sung cần biện minh, nếu không nó vi phạm YAGNI. Nói cách khác, ai đó tin rằng bạn cần nó, có một gánh nặng để chứng minh điều đó
gnat

2
Tôi có thể hiểu muốn có một lớp miền chỉ hiển thị một tập hợp con hoạt động mong muốn - ORM có xu hướng hơi rộng một diện tích bề mặt (giả sử bạn không muốn cho phép cập nhật lên một thực thể không được chỉ đạo bởi một thực thể có chứa khác). Có một lớp trừu tượng như vậy sẽ giúp với điều này.
Oded

4
Có lẽ bạn sẽ cần một lớp trừu tượng thứ hai cho lớp trừu tượng đầu tiên bên trên ORM chỉ trong trường hợp bạn cũng muốn thay đổi lớp đầu tiên.
David Peterman

1
@David Trong khi chúng tôi thêm tính năng chuyển hướng, hãy thay đổi tất cả if (boolean) của bạn thành if (boolean == true) và nếu bạn muốn lấy lại nhiều hơn như vậy, nếu (boolean == true == true ...) và v.v.
brian

Câu trả lời:


12

Đó là cách nói dối điên rồ. Rất có khả năng là bạn sẽ không cần phải thay đổi ORM. Và nếu bạn từng quyết định thay đổi ORM, chi phí viết lại ánh xạ sẽ là một phần rất nhỏ trong chi phí để phát triển và duy trì meta-ORM của riêng bạn. Tôi hy vọng rằng bạn có thể viết một vài tập lệnh để thực hiện 95% công việc cần thiết để chuyển đổi ORM.

Khung trong nhà hầu như luôn luôn là một thảm họa. Xây dựng một trong dự đoán nhu cầu trong tương lai gần như là một thảm họa được đảm bảo. Các khung thành công được trích xuất từ ​​các dự án thành công, không được xây dựng trước thời hạn để đáp ứng nhu cầu tưởng tượng.


12

ORM cung cấp một sự trừu tượng hóa cho lớp dữ liệu của bạn độc lập với RDBMS của nó, nhưng nó có thể không đủ để "gỡ" lớp doanh nghiệp của bạn khỏi lớp dữ liệu của bạn. Cụ thể, bạn không nên để các đối tượng ánh xạ tới các bảng RDBMS "rò rỉ" trực tiếp vào lớp nghiệp vụ.

Ít nhất, lớp doanh nghiệp của bạn cần lập trình cho các giao diện mà các đối tượng được ánh xạ bảng, được quản lý ORM từ lớp dữ liệu có thể có khả năng thực hiện. Ngoài ra, bạn có thể cần tạo một lớp xây dựng truy vấn trừu tượng dựa trên giao diện để ẩn khả năng truy vấn gốc của ORM. Mục tiêu chính là để tránh "nướng" bất kỳ ORM cụ thể nào vào giải pháp của bạn ngoài lớp dữ liệu của nó. Ví dụ: có thể tạo ra các chuỗi HQL ( Ngôn ngữ truy vấn Hibernate ) trong lớp nghiệp vụ. Tuy nhiên, quyết định dường như vô hại này sẽ ràng buộc lớp doanh nghiệp của bạn với Hibernate, do đó đóng đinh các lớp truy cập dữ liệu và doanh nghiệp lại với nhau; bạn nên cố gắng tránh tình trạng này càng nhiều càng tốt.

EDIT: Trong trường hợp của bạn, lớp bổ sung bên trong kho lưu trữ là một sự lãng phí thời gian: dựa trên điểm số hai của bạn, lớp doanh nghiệp của bạn được cách ly đủ từ kho lưu trữ của bạn. Cung cấp cách nhiệt bổ sung sẽ giới thiệu sự phức tạp không cần thiết, không có lợi ích bổ sung.

Vấn đề với việc xây dựng một lớp trừu tượng bổ sung bên trong kho lưu trữ của bạn là "thương hiệu" cụ thể của ORM quyết định cách bạn tương tác với nó. Nếu bạn xây dựng một trình bao bọc mỏng trông giống như ORM của bạn, nhưng nằm dưới sự kiểm soát của bạn, việc thay thế ORM cơ bản sẽ khó khăn như vậy nếu không có lớp bổ sung đó. Mặt khác, nếu bạn xây dựng một lớp trông không giống ORM của bạn, thì bạn nên đặt câu hỏi về sự lựa chọn của bạn về công nghệ ánh xạ quan hệ đối tượng.


Tôi rất vui vì .NET đã giải quyết vấn đề này bằng cách đưa Đối tượng truy vấn vào nền tảng. Cổng .NET của Hibernate thậm chí còn hỗ trợ nó.
Michael Brown

2
@MikeBrown Vâng, và .NET cũng cung cấp hai công nghệ ORM cạnh tranh của riêng mình, cả hai đều sử dụng công nghệ LINQ!
dasblinkenlight

@dasblinkenlight Tôi đã cập nhật câu hỏi để cung cấp cho bạn thông tin bổ sung.
Patrick Desjardins

Gần đây, tôi đã áp dụng cách tiếp cận để có lớp nghiệp vụ phụ thuộc vào lớp dữ liệu dựa trên các giao diện giống như bản đồ và giống như tập hợp để lưu trữ trạng thái. Bây giờ tôi tin rằng các giao diện đó thể hiện mối quan tâm giữ trạng thái đủ tốt (lấy cảm hứng từ phong cách lập trình chức năng) và cung cấp một sự tách biệt tốt đẹp khỏi ORM của sự lựa chọn thông qua việc triển khai khá mỏng.
beluchin

2

UnitOfWork thường cung cấp sự trừu tượng hóa này. Đó là một nơi cần thay đổi, kho lưu trữ của bạn phụ thuộc vào nó thông qua Giao diện. Nếu bạn cần thay đổi O / RM, chỉ cần thực hiện UoW mới trên nó. Một và thực hiện.

BTW nó vượt ra ngoài việc chuyển đổi O / RM, hãy nghĩ đến thử nghiệm đơn vị. Tôi có ba triển khai UnitOfWork, một cho EF, một cho NH (vì tôi thực sự DID phải chuyển đổi dự án giữa O / RM cho một khách hàng muốn hỗ trợ của Oracle) và một cho sự kiên trì của InMemory. Sự kiên trì của InMemory là hoàn hảo để thử nghiệm đơn vị hoặc thậm chí cho tạo mẫu nhanh trước khi tôi sẵn sàng đặt cơ sở dữ liệu phía sau nó.

Khung này là đơn giản để thực hiện. Trước tiên, bạn có giao diện IRep repository chung của bạn

public interface IRepository<T>
  where T:class
{
  IQueryable<T> FindBy(Expression<Func<T,Bool>>filter);
  IQueryable<T> GetAll();
  T FindSingle(Expression<Func<T,Bool>> filter);
  void Add(T item);
  void Remove(T item);

}

Và giao diện IUnitOfWork

public interface IUnitOfWork
{
   IQueryable<T> GetSet<T>();
   void Save();
   void Add<T>(T item) where T:class;
   void Remove<T>(T item) where T:class;
}

Tiếp theo là kho lưu trữ cơ sở (sự lựa chọn của bạn về việc nó có nên trừu tượng hay không

public abstract class RepositoryBase<T>:IRepository<T>
  where T:class
{
   protected readonly IUnitOfWork _uow;

   protected RepositoryBase(IUnitOfWork uow)
   { 
      _uow=uow;
   }

   public IQueryable<T> FindBy(Expression<Func<T,Bool>>filter)
   {
      return _uow.GetSet<T>().Where(filter);
   }

   public IQueryable<T> GetAll()
   {
      return _uow.GetSet<T>();
   }

   public T FindSingle(Expression<Func<T,Bool>> filter)
   {
      return _uow.GetSet<T>().SingleOrDefault(filter);
   }

   public void Add(T item)
   {
      return _uow.Add(item);
   }

   public void Remove(T item)
   {
      return _uow.Remove(item);
   }
}

Triển khai IUnitOfWork là trò chơi của trẻ em dành cho EF, NH và trong bộ nhớ. Lý do tôi trả lại IQueryable, là vì lý do tương tự, Ayende đã đề cập trong bài đăng của mình, khách hàng có thể lọc thêm, sắp xếp, nhóm và thậm chí dự đoán kết quả bằng LINQ và bạn vẫn nhận được lợi ích của tất cả phía máy chủ.


1
Nhưng câu hỏi ở đây là xác định liệu lớp trên có hữu ích hay không và nên là người gác cổng cho tất cả các truy cập dữ liệu.
brian

Ước gì tôi có thể trỏ đến bài đăng trên blog của mình về việc triển khai Đơn vị công việc / Kho lưu trữ. Nó thảo luận về những mối quan tâm chính xác từ OP.
Michael Brown

Đặt tên cho lớp không có nghĩa là cần thiết hoặc hữu ích.
kevin cline

Lưu ý theo OP, anh ta có thêm một ánh xạ giữa truy cập dữ liệu và lớp nghiệp vụ. Đối với tôi, các đối tượng kinh doanh và các đối tượng thực thể của tôi là như nhau. EF và NH cung cấp các API ánh xạ tuyệt vời để việc ánh xạ dữ liệu hiếm khi (nếu có) trở thành mối quan tâm.
Michael Brown

Làm thế nào để bạn dịch một biểu thức tùy ý sang một cuộc gọi ORM hiệu quả? Bạn không thể tìm nạp mọi thứ và vứt bỏ các hàng không khớp với bộ lọc.
kevin cline
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.