2013-07-24 26 views
7

Tôi sắp bắt đầu một dự án mới, sẽ sử dụng Entity Framework (EF) làm ORM để liên kết với SQL Server. Tôi đã và đang thực hiện một số lượng lớn việc đọc để hiểu cách tiếp cận tốt để làm việc với EF liên quan đến trừu tượng và cuối cùng là làm cho mã của tôi có thể kiểm thử được.Có sử dụng một Abstraction Repository trên Entity Framework Good Practice hay không?

Vấn đề là đọc nhiều hơn tôi làm bối rối hơn tôi trở nên với tất cả.

gì tôi nghĩ là một cách tốt để đi:

tôi sẽ đi xuống con đường kho, làm cho họ để họ có thể dễ dàng tiêm thông qua giao diện và dễ dàng để thử cho mục đích thử nghiệm, từ rất nhiều của các ví dụ tôi dường như tìm thấy, có rất nhiều người thề theo cách tiếp cận này. Đối với các phần lưu trữ tương tác với EF, tôi đã đi với các bài kiểm tra tích hợp vì sẽ không có logic nghiệp vụ trong Repos của tôi, tôi sẽ chuyển nó thành các lớp Service/Controller để xử lý.

Một số nói Pattern Repository là một sự trừu tượng không có cơ sở

http://ayende.com/blog/3955/repository-is-the-new-singleton

Có các liên kết khác nhưng tôi không muốn lũ câu hỏi này với quá nhiều

tôi loại được điều này, tôi hiểu rằng EF và việc thực hiện nó về bản chất là trừu tượng, nhưng với tôi nó có vẻ như là một cái cụ thể (nó chạm vào thứ gì đó bên ngoài phạm vi ứng dụng ... DB) Vấn đề đối với tôi là nó p chúng tôi có thể tương tác với các bối cảnh dữ liệu và đơn vị công việc từ bên trong Dịch vụ của tôi, nhưng cảm thấy như tôi đang trộn dữ liệu đăng nhập truy cập với logic nghiệp vụ ở cấp độ đó và sau đó làm thế nào địa ngục để tôi kiểm tra đơn vị đó?

Tôi dường như đã đọc rất nhiều đến nỗi bây giờ tôi không có hướng rõ ràng và khối lập trình không thể tránh khỏi đã được cài đặt. Tôi đang cố tìm cách tiếp cận sạch sẽ và tôi có thể kiểm tra.

CẬP NHẬT - Giải pháp tôi đang đi để bắt đầu với

tôi đã đi một con đường hơi khác nhau thì một trong các câu trả lời được đưa ra bởi simon nhưng nhờ vào một bài viết mà tôi đã đọc nhưng ông nộp một lần nữa vì vậy tôi đã đi qua nó một lần nữa. Trước hết tôi đang sử dụng Code First vì tôi có một cơ sở dữ liệu hiện có và vì một số lý do không thích các công cụ thiết kế. Tôi đã tạo ra một số đối tượng POCO đơn giản và một số ánh xạ, để giữ cho mọi thứ đơn giản ở đây tôi sẽ chỉ hiển thị một POCO/Mapping và những gì không.

POCO

public abstract class BaseEntity<T> 
{ 
    [Key] 
    public T Id { get; set; } 
    public string Name { get; set; } 
    public DateTime CreatedOn { get; set; } 
}  

public class Project : BaseEntity<int> 
{ 
    public virtual ICollection<Site> Sites { get; set; } 
    public bool Active { get; set; } 
}  

Đơn vị của giao diện làm việc

public interface IUnitOfWork 
{ 
    IDbSet<Project> Projects { get; } 

    void Commit(); 
} 

Context

internal class MyContext : DbContext 
{ 
    public MyContext(string connectionString) 
     : base(connectionString) 
    { 

    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Configurations.Add(new ProjectMap()); 
    } 
} 

bê tông UnitOfWork Mã

public class UnitOfWork : IUnitOfWork 
{ 
    readonly MyContext _context; 
    const string ConnectionStringName = "DBConn"; 

    public UnitOfWork() 
    { 
     var connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString; 
     _context = new FosilContext(connectionString); 
    } 

    public IDbSet<Project> Projects 
    { 
     get { return _context.Set<Project>(); } 
    } 

    public void Commit() 
    { 
     _context.SaveChanges(); 
    } 
} 

thử nghiệm cơ bản

 var _repo = new UnitOfWork().Projects; 
     var projects = from p in _repo 
         select p; 

     foreach (var project in projects) 
     { 
      Console.WriteLine(string.Format("{0} - {1} - {2} - {3})", project.Id, project.Name, project.Active.ToString(), project.CreatedOn.ToShortDateString())); 
     } 

     Console.Read(); 

Các UnitOfWork là tự giải thích nhưng dựa trên một bối cảnh cụ thể, tôi có thể tạo ra một IUnitOfWork giả và vượt qua nó trong một IDBSet giả để thử nghiệm . Đối với tôi, và điều này có thể trở lại để cắn tôi về mặt kiến ​​trúc, tôi càng nhận được nhiều hơn về điều này nhưng không có một Repo trên đầu trang của EF trừu tượng này đi (tôi nghĩ). Như Điều giải thích IDbSet là tương đương với một Repo để thats những gì tôi đang sử dụng. Tôi kinda ẩn bối cảnh tùy chỉnh của tôi đằng sau UoW nhưng tôi sẽ xem làm thế nào mà chảo ra.

Điều tôi đang nghĩ bây giờ là tôi có thể sử dụng trong lớp dịch vụ, sẽ đóng gói dữ liệu và quy tắc kinh doanh nhưng phải là đơn vị có thể thử nghiệm vì tôi có thể giả mạo các mục cụ thể của EF. Hy vọng rằng sẽ làm cho các bộ điều khiển của tôi khá nạc, nhưng chúng ta sẽ thấy :)

+0

Đừng quên rằng nếu bạn đang sử dụng khung thực thể, 'DbContext' _is_ giống như một UnitOfWork và' DbSet' _is_ giống như một Kho lưu trữ. Câu hỏi này, tôi nghĩ rằng, lời mời cho ý kiến ​​cá nhân và có thể không phù hợp cho Stack Overflow nhưng đó là ý kiến ​​của tôi rằng nếu bạn đang sử dụng Entity Framework, bạn cũng có thể nắm lấy mẫu. Việc gói thêm 'DbContext' và' DbSet 'thực sự là để cho một sự trừu tượng hóa rất mỏng trên lớp bê tông để dễ kiểm thử đơn vị và, ở một mức độ nhất định, Dependency Injection. –

+0

Cảm ơn nhận xét. Những gì tôi không nhận được (tôi rất mới để EF) là những gì lớp mỏng này sẽ như thế nào. Tôi đã xem một đoạn video cụ thể về EF và thử nghiệm và tác giả đã sử dụng IDBSet và giới thiệu một giao diện IContext, nhưng với một đơn vị công việc riêng biệt (những ví dụ này đã sử dụng một kho lưu trữ) đã được chuyển. Lớp mỏng là IDBSet và IContext. tác giả lưu ý rằng bạn đã giấu đi EF với những trừu tượng này dường như giống hệt như vấn đề mọi người có với Mẫu Kho lưu trữ? – Modika

+0

Tôi đang di chuyển bức tường văn bản trở lại câu trả lời trước của tôi. Các hạn chế về ký tự khiến định dạng hơi khó. –

Trả lời

7

Đừng quên rằng nếu bạn đang sử dụng Entity Framework, DbContext giống như một UnitOfWork và DbSet giống như một Repository. Câu hỏi này, tôi nghĩ rằng, lời mời cho ý kiến ​​cá nhân và có thể không phù hợp cho Stack Overflow nhưng đó là ý kiến ​​của tôi rằng nếu bạn đang sử dụng Entity Framework, bạn cũng có thể nắm lấy mẫu. Gói thêm DbContextDbSet s thực sự mang lại sự trừu tượng rất mỏng trên lớp bê tông để dễ dàng kiểm thử đơn vị và, ở một mức độ nhất định, Dependency Injection.

Có một chủ đề trên MSDN (Testability and Entity Framework 4.0) có thể cung cấp cho bạn một điểm khởi đầu tốt. Có một vài tài nguyên ở đây và có gợi ý về cách thực hiện mẫu này trong bối cảnh khung thực thể. Trong hầu hết trường hợp, bạn sẽ sử dụng DbContext để thực hiện SaveChanges()DbSet<T> để thực hiện các hoạt động CRUD của bạn với sự trợ giúp của IQueryable<T> triển khai trên DbSet<T>. Bạn có thể triển khai IUnitOfWork của mình để bắt chước những gì bạn cần trên số DbContext của bạn và tương tự cho DbSet.

An thực hiện trong trường hợp đó sẽ gần như là một bản đồ 1-to-1 trong những phương pháp trong DbContextDbSet<T>, nhưng bạn cũng có thể thực hiện một IUnitOfWork tùy chỉnh mà thực hiện In-Memory dựa IRepository<T> (sử dụng HashSet s chẳng hạn) mà tạo điều kiện thử nghiệm đơn vị của logic truy vấn đối với các thành phần có thể truy vấn. Mô hình kho lưu trữ có phải là một ý tưởng hay không? Đây là điều gây tranh cãi. Nó thực sự phụ thuộc vào các dự án và sự linh hoạt bạn cần và những gì bạn muốn các lớp khác nhau của bạn để làm (và những gì bạn không muốn).

Chỉnh sửa để trả lời bình luận: thực hiện UnitOfWork của bạn không nên kế thừa DbContext nhưng tạo ra một thể hiện, giữ riêng tư và ủy lời gọi phương thức riêng của mình với bối cảnh. Việc triển khai Repository<T> của bạn sẽ được thực hiện một cách hợp lý theo số DbSet<T> trong một hàm tạo nội bộ. Một trong những lợi thế lớn của phương pháp Model-First là sự tự do cho tổ chức lắp ráp. Những gì tôi thường kết thúc làm là để tách các mô hình và DbContext trong hai hội đồng riêng biệt, sau này tham khảo trước đây. Kịch bản này sẽ như thế nào:

1 - lắp ráp Models.dll của bạn có chứa POCO bạn

2 - lắp ráp Data.dll của bạn có chứa các giao diện và thực hiện của bạn. Bạn cũng có thể có (ví dụ) Data.Core.dll có chứa giao diện của bạn và các tiện ích phổ biến liên quan đến lớp truy cập dữ liệu của bạn và có một Data.Entity.dll hoán đổi cho nhau (hoặc Data.List.dll) để thực hiện thực tế của bạn. Nếu chúng ta đi với các tùy chọn đầu tiên, nó có thể trông giống như:

public interface IUnitOfWork { /* Your methods */ } 

public interface IRepository<T> where T : class { /* Your methods */ } 

internal class YourDbContext : DbContext { /* Your implementation */ } 

public class YourDatabaseContext : IUnitOfWork 
{ 
    private readonly YourDbContext dbContext; 

    public YourDatabaseContext() 
    { 
     // You could also go with the Lazy pattern here to defer creation 
     dbContext = new YourDbContext(); 
    } 
} 

internal class DbSetRepository<T> : IRepository<T> where T : class 
{ 
    private readonly DbSet<T> dbSet; 

    public DbSetRepository(DbSet<T> dbSet) 
    { 
     // You could also use IDbSet<T> for a toned down version 
     this.dbSet = dbSet; 
    } 
} 

Trong tình huống này, chỉ có giao diện của bạn và thực hiện IUnitOfWork của bạn là bên ngoài có thể nhìn thấy của hội đồng (nếu bạn muốn sử dụng Dependency Injection, ví dụ). Điều này không quan trọng và thực sự là vấn đề lựa chọn thiết kế. Vì DbContext của bạn sẽ được "liên kết" với POCO của bạn theo định nghĩa về việc triển khai nội bộ của bạn và mọi thứ sẽ được kết nối trong cấu hình của bạn.

+0

Cảm ơn bạn đã liên kết, tôi đã đọc ngày hôm qua, nhưng tôi nghĩ rằng tôi đã nhầm lẫn với việc đặt tên khi họ sử dụng UoW thay vì Ngữ cảnh nhưng có vẻ hoán đổi cho nhau ở đây. Một điều (nhưng điều này là không thực sự liên quan đến điều này) là tôi không thể tìm thấy một nơi để thêm ánh xạ để liên kết mô hình của tôi với cơ sở dữ liệu đó là lý do tại sao tôi đã cố gắng để tìm nơi khác. – Modika

+0

@Modika Bạn có ý gì khi ánh xạ? EDMX của bạn? –

+0

Tôi không sử dụng EDMX vì tôi đã đi xuống phương pháp tiếp cận mã đầu tiên, nhưng ánh xạ kết nối các POCO của tôi với biểu diễn cơ sở dữ liệu. Tôi có một loạt các ánh xạ EntityTypeConfiguration, nhưng tôi nghĩ, nhìn vào bài viết đó, việc triển khai cụ thể ngữ cảnh (UoF) có thể đơn giản kế thừa từ DbContext và sau đó tôi có quyền truy cập vào phương thức OnModelCreating ... không chắc chắn liệu nó có hoạt động hay không ... – Modika

0

Chúng tôi đã sử dụng khung trừu tượng ORM riêng của chúng tôi hỗ trợ kho lưu trữ, đơn vị công việc và các mẫu đặc điểm kỹ thuật trong một vài năm và dựa trên kinh nghiệm của chúng tôi.

Sự trừu tượng như vậy không nhất thiết đảm bảo khả năng thay thế. Một lợi ích rất quan trọng khác là khả năng mở rộng. Ví dụ: đối với một dự án, sau khi phát hành, yêu cầu mới là thuộc tính "Đã xuất bản" trên một số thực thể và giao diện người dùng phải ẩn các thực thể chưa được xuất bản. Đối với kịch bản này, chúng tôi đã mở rộng khung công tác của mình để hỗ trợ "đặc tả mặc định" rất giống với "bộ lọc phiên" của Hibernate áp dụng cho tất cả các truy vấn được thực hiện trên một DbContext.

Kể từ khi chúng tôi đã có một "yêu cầu scoped" hệ thống vòng đời DbContext (mà thực chất là cách ưa thích hiện nay trong các ứng dụng cốt lõi dựa owin và .net), nó rất dễ dàng cho chúng tôi để thêm một API như:

CurrentContext.AddDefaultSpec(new Spec<Product>(t => t.Published)) 

cuộc gọi này cho biết thêm spec đưa vào danh sách các thông số kỹ thuật sản phẩm trong bối cảnh hiện nay và áp dụng cho tất cả các thông số kỹ thuật sản phẩm để truy vấn biểu thức thực thi trên

Repository<Product>.Query().Where(....) 

Tất nhiên hệ thống đặc tả của chúng tôi là đã có khả năng sáp nhập biểu thức từ nhiều lambdas và thông số kỹ thuật nhưng nó nằm ngoài phạm vi ở đây.

Để thực hiện chức năng này, chúng tôi đã phát triển một thuộc tính bộ lọc ASP .NET MVC đơn giản và trong hầu hết các hành động điều khiển của chúng tôi (trừ các phần quản trị), chúng tôi đặt thuộc tính này. Một lưu ý: kể từ khi trừu tượng hóa cả hai phương pháp đồng bộ và không đồng bộ là rất khó và không đáng nỗ lực, và chúng tôi cũng cần một số cuộc gọi cụ thể của EF như .Include() vv, mã máy khách vẫn có tham chiếu đến EF không giúp đỡ nếu chúng ta cần thay thế EF bằng một ORM khác. Nhưng đây không phải là một yêu cầu và trong thực tế bạn không đưa ra yêu cầu như vậy trong hầu hết các dự án.Vì vậy, nếu lý do để tóm tắt nó với một kho lưu trữ chỉ là tạo một API nền tảng "lý tưởng" độc lập, và nếu bạn không xây dựng một khung chung để nhắm vào các ORM hoặc giao diện truy cập dữ liệu khác nhau, tôi không nghĩ rằng nó là đúng cách. Mục đích chính của chúng tôi là tạo ra một khuôn khổ nội bộ dựa trên EF với các tiện ích bổ sung, hỗ trợ cho các mẫu thiết kế phổ biến và đơn vị sự kiện tùy biến của các công trình (tức là quan sát các sự kiện khi một số loại đối tượng được thêm vào uow) đó là một giải pháp phù hợp cho vấn đề trong tay. Mặc dù nó không phải là rất dễ dàng để thực hiện.

Các vấn đề liên quan