Nếu Mô hình lưu trữ quá mức cần thiết cho các ORM hiện đại (EF, nHibernate), thì sự trừu tượng hóa tốt hơn là gì?


12

Gần đây tôi đã đọc rất nhiều đối số chống lại việc sử dụng mẫu kho lưu trữ với Entity Framework mạnh mẽ của ORM vì nó kết hợp chức năng giống như kho lưu trữ, cùng với chức năng Đơn vị công việc.

Một lập luận khác chống lại việc sử dụng mẫu cho một tình huống như kiểm thử đơn vị là mẫu kho lưu trữ là một sự trừu tượng hóa rò rỉ do các triển khai chung hơn tận dụng IQueryable.

Các đối số chống lại việc sử dụng mẫu kho lưu trữ có ý nghĩa với tôi, nhưng các phương pháp trừu tượng thay thế được đề xuất thường gây nhầm lẫn hơn và xuất hiện cũng quá mức như vấn đề.

Giải pháp của Jimmy Bogards dường như là một sự pha trộn của việc thổi bay sự trừu tượng, nhưng cũng giới thiệu kiến ​​trúc của riêng ông. https://lostechies.com/jimmybogard/2012/10/08/favor-query-objects-over-repose khu /

Một ví dụ khác về kho lưu trữ là không cần thiết .... nhưng sử dụng kiến ​​trúc của tôi! http://blog.gauffin.org/2012/10/22/griffin-decoupling-the-queries/

Một ... http://www.thereformedprogrammer.net/is-the-reposeective-potype-usiously-with-entity-framework

Tôi đã không tìm thấy một sự thay thế rõ ràng hoặc thay thế cho cách tiếp cận mẫu kho lưu trữ "quá phức tạp" mà bản thân nó không được kiến ​​trúc nhiều hơn.


4
Cụ thể bạn đang cố gắng đạt được điều gì? Trừu tượng nên có một mục đích. Nếu bạn đang viết một ứng dụng CRUD, ORM có lẽ đủ trừu tượng.
JacquesB

@JacquesB Tôi đang cố gắng tránh các vấn đề trở kháng quan hệ đối tượng với một mô hình miền mạnh mẽ, nhưng cũng trừu tượng tránh xa các chế độ xem của tôi trong triển khai mvc.
AnotherDeveloper 18/03/2016

Reed Cospey có rất nhiều điều tích cực để nói về IQuerable ở đây: stackoverflow.com/questions/1578778/USE-iqueryable-with-linq Điều này ngụ ý nó tốt hơn ở lớp vận chuyển. Theo như sự trừu tượng hóa, tôi thấy việc sử dụng cho mẫu Kho lưu trữ chung hoạt động tốt khi tôi cần tiêm EntityType nhưng vẫn muốn duy trì các phương thức phổ biến. Mặt khác, bản thân tôi đã tranh luận trên diễn đàn MSDN LINQ rằng EF là một mẫu kho lưu trữ vì tất cả nằm trong bộ nhớ. Một dự án đã sử dụng nhiều mệnh đề Trường hợp như các cuộc gọi phương thức hoạt động tốt.
John Peters

Nhưng một lĩnh vực tôi đã xem xét nhưng bị từ chối là kinh doanh Cây biểu hiện, một số thực sự thích nó, nhưng .... bạn phải nghiên cứu kỹ trước khi bạn thấy nó hữu ích.
John Peters

2
chúng ta nên quay lại gọi SQL từ các bộ điều khiển
bobek

Câu trả lời:


12

Tôi nghĩ rằng bạn đang lưu trữ các kho lưu trữ và kho chung chung.

Một kho lưu trữ cơ bản chỉ giao diện lưu trữ dữ liệu của bạn và cung cấp các phương thức để trả về dữ liệu

IRepository {
   List<Data> GetDataById(string id);
}

Nó không rò rỉ lớp dữ liệu vào mã của bạn thông qua một IQueryable hoặc các cách khác để chuyển các truy vấn ngẫu nhiên và cung cấp một bề mặt phương thức có thể kiểm tra và có thể tiêm được xác định rõ.

Kho lưu trữ chung cho phép bạn chuyển vào truy vấn của mình giống như ORM

IGenericRepository<T> {
    List<T> Get<T>(IQuery query);
    //or
    IQueryable<T> Get<T>();
}

Tôi đồng ý không có nhiều điểm khi sử dụng Kho lưu trữ chung trên đầu ORM mà về cơ bản chỉ là Kho lưu trữ chung khác.

Câu trả lời là sử dụng mẫu Kho lưu trữ cơ bản để ẩn ORM của bạn


1
Một ORM của một ORM? Tôi đã cố gắng để vui vẻ. Tại sao bạn cần trừu tượng hóa ORM?
johnny

1
cùng lý do bạn trừu tượng bất cứ điều gì. để tránh làm ô nhiễm mã của bạn với các lớp độc quyền
Ewan

6

Hầu hết các đối số bạn đề cập đến thuộc tính sai cho các tính năng mẫu Kho lưu trữ mà nó không có.

Về mặt khái niệm, Kho lưu trữ như được xác định ban đầu trong DDD chỉ là một tập hợp các đối tượng mà bạn có thể tìm kiếm hoặc thêm vào. Cơ chế bền bỉ đằng sau nó được trừu tượng hóa, vì vậy khi là người tiêu dùng bạn sẽ ảo tưởng rằng đó là một bộ sưu tập trong bộ nhớ.

Việc triển khai Kho lưu trữ có sự trừu tượng bị rò rỉ ( IQueryablesví dụ hiển thị) là việc triển khai Kho lưu trữ kém.

Việc triển khai Kho lưu trữ hiển thị nhiều hơn chỉ các hoạt động thu thập (ví dụ: các tính năng Đơn vị công việc) là một triển khai Kho lưu trữ kém.

Có những lựa chọn thay thế cho Kho lưu trữ để truy cập dữ liệu? Có, nhưng chúng không liên quan đến các vấn đề bạn đưa ra trong câu hỏi của bạn.



1

Đối với tôi, các kho lưu trữ, kết hợp với ORM hoặc các lớp lưu giữ DB khác, có những nhược điểm sau:

  1. Bao gồm các đơn vị công việc. UoW phải được lập trình viên mã hóa và hiếm khi có thể được triển khai như một loại phép thuật trong nền, nơi người dùng chỉ cần thực hiện các truy vấn và sửa đổi, mà không xác định ranh giới UoW và có thể là điểm cam kết. Đôi khi, UoW bị bỏ qua bằng cách giảm chúng thành micro UoW (ví dụ: phiên NHibernate) trong mỗi phương thức truy cập Kho lưu trữ.
  2. Che đậy, hoặc, trong trường hợp xấu nhất, tiêu diệt Sự thiếu hiểu biết dai dẳng: Các phương thức như "Tải ()", "Nhận ()", "Lưu ()" hoặc "Cập nhật ()" đề xuất các thao tác đối tượng đơn lẻ, ngay lập tức, như thể gửi từng cá nhân SQL / DML hoặc như thể làm việc với các tệp. Trong thực tế, ví dụ, các phương thức NHibernate, với các tên gây hiểu lầm này, thường không tạo quyền truy cập riêng lẻ, nhưng hãy sử dụng tải lười biếng hoặc chèn / cập nhật hàng loạt (Sự thiếu hiểu biết dai dẳng). Đôi khi, các lập trình viên tự hỏi tại sao họ không nhận được các hoạt động DB ngay lập tức và buộc phải phá vỡ sự thiếu hiểu biết dai dẳng, do đó giết chết hiệu suất và sử dụng các nỗ lực lớn để thực sự làm cho hệ thống trở nên tồi tệ hơn.
  3. Tăng trưởng không kiểm soát. Một kho lưu trữ đơn giản có thể tích lũy ngày càng nhiều phương thức để phù hợp với nhu cầu cụ thể.

Nhu la:

public interface ICarsRepository  /* initial */
{
    ICar CreateNewCar();
    ICar LoadCar(int id); // bad, should be for multiple IDs.
    void SaveCar(ICar carToSave); // bad, no individual saves, use UoW commit!
}

public interface ICarsRepository  /* a few years later */
{
    ICar CreateNewCar();
    ICar LoadCar(int id); 
    IList<ICar> GetBlueCars();
    IList<ICar> GetRedYellowGreenCars();
    IList<ICar> GetCarsByColor(Color colorOfCars); // a bit better
    IList<ICar> GetCarsByColor(IEnumerable<Color> colorsOfCars); // better!
    IList<ICar> GetCarsWithPowerBetween(int hpFrom, int hpTo);
    IList<ICar> GetCarsWithPowerKwBetween(int kwFrom, int kwTo);
    IList<ICar> GetCarsBuiltBetween(int yearFrom, int yearTo);
    IList<ICar> GetCarsBuiltBetween(DateTime from, DateTime to); // some also need month and day
    IList<ICar> GetHybridCarsBuiltBetween(DateTime from, DateTime to); 
    IList<ICar> GetElectricCarsBuiltBetween(DateTime from, DateTime to); 
    IList<ICar> GetCarsFromManufacturer(IManufacturer carManufacturer); 
    bool HasCarMeanwhileBeenChangedBySomebodyElseInDb(ICar car); // persistence ignorance broken
    void SaveCar(ICar carToSave);
}

4. Đối tượng nguy hiểm của Chúa: bạn có thể bị cám dỗ tạo một lớp thần, bao trùm tất cả mô hình hoặc lớp truy cập dữ liệu của bạn. Lớp kho lưu trữ không chỉ chứa các phương thức Car mà còn các phương thức cho tất cả các thực thể.

Theo tôi, tốt hơn là cung cấp ít nhất một số cơ hội truy vấn, để tránh sự lộn xộn lớn của nhiều phương pháp mục đích duy nhất. Bất kể đó là LINQ, ngôn ngữ truy vấn riêng hay thậm chí là thứ được lấy trực tiếp từ ORM (OK, loại vấn đề ghép nối ...).


4
Tất cả các phương thức này sẽ đi đâu nếu không sử dụng Mẫu Kho lưu trữ?
johnny


1

Nếu mục đích của giao diện Kho lưu trữ là để giả lập cơ sở dữ liệu cho một unittest (= test trong sự cô lập) thì sự trừu tượng tốt nhất là thứ dễ bị giả định.

Nó là khác nhau để giả định một giao diện kho lưu trữ dựa trên kết quả IQueryable.

Từ quan điểm kiểm tra đơn vị

IRepository {
   List<Data> GetDataById(string id);
}

có thể bị chế giễu một cách dễ dàng

IGenericRepository<T> {
    List<T> Get<T>(IQuery query);
}

chỉ có thể được mô phỏng dễ dàng nếu giả lập bỏ qua nội dung của tham số truy vấn.

IGenericRepository<T> {
    IQueryable<T> Get<T>(some_parameters);
}

không thể dễ dàng bị chế giễu


0

Tôi không nghĩ rằng mô hình kho lưu trữ là quá mức, nếu bạn sử dụng các hàm lambda để truy vấn. Đặc biệt là khi bạn phải trừu tượng ORM (theo ý kiến ​​của tôi bạn luôn luôn nên) thì tôi sẽ không quan tâm đến các chi tiết triển khai của chính kho lưu trữ.

Ví dụ:

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        UserRepository ur = new UserRepository();
        var userWithA = ur.GetBy(u => u.Name.StartsWith("A"));

        Console.WriteLine(userWithA.Name);


        ur.GetAllBy(u => u.Name.StartsWith("M"))
          .ForEach(u => Console.WriteLine(u.Name));


        ur.GetAllBy(u => u.Age > 13)
          .ForEach(u => Console.WriteLine(u.Name));
    }
}

public class UserRepository 
{
    List<User> users = new List<User> { 
        new User{Name="Joe", Age=10},
            new User{Name="Allen", Age=12},
            new User{Name="Martin", Age=14},
            new User{Name="Mary", Age=15},
            new User{Name="Ashton", Age=29}
    };

    public User GetBy(Predicate<User> userPredicate)
    {
        return users.Find(userPredicate);
    }

    public List<User> GetAllBy(Predicate<User> userPredicate)
    {
        return users.FindAll(userPredicate);
    }
}

public class User
{
    public string Name { get; set; }

    public int Age { get; set; }
}
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.