2008-11-07 23 views
11

Tôi đã cố gắng để tìm ra một cách để viết kho chung mà làm việc chống lại các cửa hàng dữ liệu khác nhau:Có phải tất cả chúng ta đang tìm kiếm cùng một IRepository?

public interface IRepository 
{ 
    IQueryable<T> GetAll<T>(); 
    void Save<T>(T item); 
    void Delete<T>(T item); 
} 
public class MemoryRepository : IRepository {...} 
public class SqlRepository : IRepository {...} 

tôi muốn làm việc với các lớp miền POCO giống nhau trong mỗi. Tôi cũng đang xem xét một cách tiếp cận tương tự, trong đó mỗi lớp miền có kho riêng của nó:

public interface IRepository<T> 
{ 
    IQueryable<T> GetAll(); 
    void Save(T item); 
    void Delete(T item); 
} 
public class MemoryCustomerRepository : IRepository {...} 
public class SqlCustomerRepository : IRepository {...} 

Câu hỏi của tôi: 1) là phương pháp đầu tiên thậm chí khả thi? 2) Có lợi thế nào cho phương pháp thứ hai không.

Trả lời

5
  1. Phương pháp đầu tiên là khả thi, tôi đã làm điều gì đó tương tự trong quá khứ khi tôi đã viết khuôn khổ lập bản đồ của riêng tôi mà nhắm RDBMS và XmlWriter/XmlReader. Bạn có thể sử dụng cách tiếp cận này để dễ dàng kiểm tra đơn vị, mặc dù tôi nghĩ bây giờ chúng tôi có các công cụ OSS cấp trên để thực hiện điều đó.

  2. Cách tiếp cận thứ hai là những gì tôi hiện đang sử dụng ngay bây giờ với IBATIS.NET mappers. Mỗi người lập bản đồ có một giao diện và mọi người lập bản đồ [có thể] cung cấp các hoạt động CRUD cơ bản của bạn. Ưu điểm là mỗi người lập bản đồ cho một lớp miền cũng có các chức năng cụ thể (chẳng hạn như SelectByLastName hoặc DeleteFromParent) được thể hiện bằng một giao diện và được xác định trong trình ánh xạ cụ thể. Bởi vì điều này không cần thiết cho tôi để triển khai các kho lưu trữ riêng biệt như bạn đang đề xuất - những người lập bản đồ cụ thể của chúng tôi nhắm mục tiêu cơ sở dữ liệu. Để thực hiện các kiểm tra đơn vị, tôi sử dụng StructureMapMoq để tạo các kho lưu trữ trong bộ nhớ hoạt động như Memory*Repository của bạn. Các lớp học ít hơn của nó để thực hiện và quản lý và làm việc ít hơn tổng thể cho một cách tiếp cận rất có thể kiểm chứng. Đối với dữ liệu được chia sẻ qua các bài kiểm tra đơn vị, tôi sử dụng một mẫu trình xây dựng cho mỗi lớp miền có các phương thức WithXXX và các phương thức AsSomeProfile (các AsSomeProfile chỉ trả về phiên bản trình xây dựng với dữ liệu thử nghiệm được định cấu hình trước).

Dưới đây là một ví dụ về những gì tôi thường kết thúc với trong các thử nghiệm đơn vị của tôi:

// Moq mocking the concrete PersonMapper through the IPersonMapper interface 
var personMock = new Mock<IPersonMapper>(MockBehavior.Strict); 
personMock.Expect(pm => pm.Select(It.IsAny<int>())).Returns(
    new PersonBuilder().AsMike().Build() 
); 

// StructureMap's ObjectFactory 
ObjectFactory.Inject(personMock.Object); 

// now anywhere in my actual code where an IPersonMapper instance is requested from 
// ObjectFactory, Moq will satisfy the requirement and return a Person instance 
// set with the PersonBuilder's Mike profile unit test data 
4

Trên thực tế có một sự đồng thuận chung bây giờ mà kho tên miền không nên chung chung. Kho lưu trữ của bạn nên thể hiện những gì bạn có thể làm khi lưu giữ hoặc truy lục các thực thể của bạn.

Một số kho được readonly, một số được chèn chỉ (không cập nhật, không xóa), một số có tra cứu chỉ cụ thể ...

Sử dụng một GetAll trở IQueryable, logic truy vấn của bạn sẽ bị rò rỉ vào mã của bạn, có thể để lớp ứng dụng. Tuy nhiên, vẫn thú vị khi sử dụng loại giao diện mà bạn cung cấp để gói gọn các đối tượng LINQ Table<T> để bạn có thể thay thế nó bằng việc triển khai bộ nhớ cho mục đích thử nghiệm.

Vì vậy, tôi đề nghị, để gọi nó là ITable<T>, cung cấp cho nó cùng một giao diện mà đối tượng LINQ Table<T>, và sử dụng nó bên kho lĩnh vực cụ thể của bạn (không thay vì).

Sau đó, bạn có thể sử dụng các kho lưu trữ cụ thể trong bộ nhớ bằng cách sử dụng bộ nhớ ITable<T> trong bộ nhớ.

Cách đơn giản nhất để triển khai ITable<T> trong bộ nhớ là sử dụng List<T> và nhận giao diện IQueryable<T> bằng cách sử dụng phương thức mở rộng .AsQueryable().

public class InMemoryTable<T> : ITable<T> 
{ 
    private List<T> list; 
    private IQueryable<T> queryable; 

    public InMemoryTable<T>(List<T> list) 
    { 
     this.list = list; 
     this.queryable = list.AsQueryable(); 
    } 

    public void Add(T entity) { list.Add(entity); } 
    public void Remove(T entity) { list.Remove(entity); } 

    public IEnumerator<T> GetEnumerator() { return list.GetEnumerator(); } 

    public Type ElementType { get { return queryable.ElementType; } } 
    public IQueryProvider Provider {  get { return queryable.Provider; } } 
    ... 
} 

Bạn có thể làm việc trong cơ sở dữ liệu để thử nghiệm, nhưng với kho lưu trữ cụ thể đúng cung cấp thêm thông tin chi tiết về miền.

2

Điều này hơi muộn ... nhưng hãy xem triển khai IRepository tại CommonLibrary.NET trên codeplex. Nó có một bộ tính năng khá tốt.

Về vấn đề của bạn, tôi thấy rất nhiều người sử dụng các phương pháp như GetAllProducts(), GetAllEmployees() trong việc triển khai kho lưu trữ của họ. Điều này là thừa và không cho phép kho lưu trữ của bạn trở thành chung chung. Tất cả những gì bạn cần là GetAll() hoặc All(). Các giải pháp được cung cấp ở trên không giải quyết được vấn đề đặt tên mặc dù.

Điều này được lấy từ tài liệu CommonLibrary.NET trực tuyến:

0.9.4 Beta 2 có triển khai Kho lưu trữ mạnh mẽ.

* Supports all CRUD methods (Create, Retrieve, Update, Delete) 
* Supports aggregate methods Min, Max, Sum, Avg, Count 
* Supports Find methods using ICriteria<T> 
* Supports Distinct, and GroupBy 
* Supports interface IRepository<T> so you can use an In-Memory table for unit-testing 
* Supports versioning of your entities 
* Supports paging, eg. Get(page, pageSize) 
* Supports audit fields (CreateUser, CreatedDate, UpdateDate etc) 
* Supports the use of Mapper<T> so you can map any table record to some entity 
* Supports creating entities only if it isn't there already, by checking for field values. 
Các vấn đề liên quan