17

Sau khi đọc thêm và nhiều hơn nữa về IoC container, tôi đọc this post về việc không có IoC.Resolve() vv trong mã của bạn.Xóa phụ thuộc vào IoC Container

Tôi thực sự tò mò muốn biết sau đó, làm cách nào để xóa phụ thuộc vào vùng chứa?

tôi sẽ muốn viết mã như sau:

public void Action() 
{ 
    using(IDataContext dc = IoC.Resolve<IDataContext>()) 
    { 
     IUserRepository repo = IoC.Resolve<IUserRepository>(); 
     // Do stuff with repo... 
    } 
} 

Nhưng làm thế nào tôi có thể thoát khỏi những IoC.Resolve gọi? Có lẽ tôi cần hiểu rõ hơn về DI ...

Xin cảm ơn trước.

Trả lời

17

Nói chung, hầu hết các phụ thuộc có thể được đưa vào lớp học của bạn tại thời điểm được tạo. Tuy nhiên, trong trường hợp cụ thể này, bạn cần một thành phần phải được tạo theo yêu cầu tại thời điểm sử dụng. Trong những trường hợp như vậy, rất khó để loại bỏ hoàn toàn sự phụ thuộc vào một thùng chứa IoC. Cách tiếp cận của tôi luôn luôn là tạo ra một nhà máy được tiêm vào lớp tại thời điểm tạo ra, do đó đóng gói tất cả việc sử dụng IoC trực tiếp. Điều này cho phép các nhà máy của bạn để nhạo báng để thử nghiệm, chứ không phải là container IoC bản thân ... mà có xu hướng được dễ dàng hơn rất nhiều:

// In Presentation.csproj 
class PresentationController 
{ 
    public PresentationController(IDataContextFactory dataContextFactory, IRepositoryFactory repositoryFactory) 
    { 
     #region .NET 4 Contract 
     Contract.Requires(dataContextFactory != null); 
     Contract.Requires(repositoryFactory != null); 
     #endregion 

     _dataContextFactory = dataContextFactory; 
     _repositoryFactory = repositoryFactory; 
    } 

    private readonly IDataContextFactory _dataContextFactory; 
    private readonly IRepositoryFactory _repositoryFactory; 

    public void Action() 
    { 
     using (IDataContext dc = _dataContextFactory.CreateInstance()) 
     { 
      var repo = _repositoryFactory.CreateUserRepository(); 
      // do stuff with repo... 
     } 
    } 
} 

// In Factories.API.csproj 
interface IDataContextFactory 
{ 
    IDataContext CreateInstance(); 
} 

interface IRepositoryFactory 
{ 
    IUserRepository CreateUserRepository(); 
    IAddressRepository CreateAddressRepository(); 
    // etc. 
} 

// In Factories.Impl.csproj 
class DataContextFactory: IDataContextFactory 
{ 
    public IDataContext CreateInstance() 
    { 
     var context = IoC.Resolve<IDataContext>(); 
     // Do any common setup or initialization that may be required on 'context' 
     return context; 
    } 
} 

class RepositoryFactory: IRepositoryFactory 
{ 
    public IUserRepository CreateUserRepository() 
    { 
     var repo = IoC.Resolve<IUserRepository>(); 
     // Do any common setup or initialization that may be required on 'repo' 
     return repo; 
    } 

    public IAddressRepository CreateAddressRepository() 
    { 
     var repo = IoC.Resolve<IAddressRepository>(); 
     // Do any common setup or initialization that may be required on 'repo' 
     return repo; 
    } 

    // etc. 
} 

Lợi ích của phương pháp này là, trong khi bạn không thể hoàn toàn loại bỏ sự phụ thuộc IoC chính nó, bạn có thể gói gọn nó trong một loại đối tượng đơn lẻ (một nhà máy), tách phần lớn mã của bạn khỏi thùng chứa IoC. Điều này cải thiện sự nhanh nhẹn mã của bạn trong ánh sáng, ví dụ, chuyển từ một thùng chứa IoC sang một thùng chứa IoC khác (ví dụ: Windsor thành Ninject).

Cần lưu ý, một hệ quả thú vị của việc này là các nhà máy của bạn thường được tiêm vào người phụ thuộc của họ bằng cùng một khuôn khổ IoC mà họ sử dụng. Ví dụ, nếu bạn đang sử dụng Castle Windsor, bạn sẽ tạo cấu hình để cho container IoC tiêm hai nhà máy vào thành phần kinh doanh của bạn khi nó được tạo ra. Bản thân thành phần kinh doanh cũng có thể có một nhà máy ... hoặc, nó có thể được tiêm một cách đơn giản bởi cùng một khung công tác IoC vào một thành phần mức cao hơn, v.v., quảng cáo inf.

+0

Cảm ơn câu trả lời. Vấn đề duy nhất là IoC mà tôi đang sử dụng phải có một phạm vi, liên quan đến câu lệnh 'using'. Sau đó, nếu tôi Resolve nói 'IDataContext' nó sẽ giải quyết * cá thể duy nhất * cho phạm vi cụ thể đó. Tôi không muốn bộ điều khiển của tôi, vv để được nhận thức của một container IoC, nhưng có thực sự bất kỳ cách nào xung quanh này? – TheCloudlessSky

+0

Điều tôi tự hỏi là nếu một Controller có thể gọi IoC.Resolve ? Nếu không, ai nên thực hiện cuộc gọi này? – TheCloudlessSky

+0

Bạn có thể giải thích thêm một chút về phạm vi này không? Bạn đang sử dụng container IoC nào? Nói chung, việc ghép bất kỳ mã nào của bạn vào khung chứa theo bất kỳ cách nào là một loại khớp nối tiêu cực ... bạn nên tránh điều đó bằng mọi giá. Theo kinh nghiệm của tôi, một phạm vi (hoặc bối cảnh) là hiếm khi, nếu bao giờ, cần thiết cho một container IoC để làm việc. Nếu nó hoạt động theo cách đó, tôi sẽ tìm một container thay thế, hoặc tìm cách cung cấp bối cảnh đó cho các nhà máy xử lý các đối tượng của bạn và giữ cho khung IoC của bạn được tách rời càng nhiều càng tốt. – jrista

1

Có một vòi phun phụ thuộc thứ hai để tiêm đầu tiên, và có đầu tiên tiêm thứ hai.

+0

Bạn có thể cười, nhưng tôi đã thấy mọi người ủng hộ điều này, một cách cẩn thận trong sự nghiêm túc. – mschaef

2

Tôi đã ở trên một dự án cách đây không lâu đã không giải quyết trên một container IoC. Họ quản lý sự không chắc chắn bằng cách không cho phép các đặc tính cụ thể không phải IoC của container của họ và cũng bằng cách gói Resolve với lớp riêng của họ. Đây cũng là một cái gì đó tôi đã nhìn thấy ủng hộ một vài lần trong bài viết blog ... loại bỏ sự phụ thuộc cuối cùng, sự phụ thuộc vào container tiêm phụ thuộc.

Đây là một kỹ thuật khả thi, nhưng tại một số điểm bạn phải chọn các công cụ bạn sử dụng và sẵn sàng chấp nhận rằng bạn sẽ trả chi phí để chuyển sang các công cụ thay thế. Đối với tôi, các thùng chứa IoC rơi vào thể loại những thứ bạn có lẽ nên ôm lấy hết lòng, vì vậy tôi đặt câu hỏi về mức độ bao thanh toán này. Nếu bạn muốn xem xét này hơn nữa, tôi đề nghị liên kết sau:

http://blog.objectmentor.com/articles/2010/01/17/dependency-injection-inversion

2

Một thay thế là để viết lại phương pháp để chấp nhận Func<T> đại biểu.Điều này loại bỏ sự phụ thuộc từ phương pháp này và cho phép bạn kiểm tra đơn vị nó với một giả:

public void Action(Func<IDataContext> getDataContext, Func<IUserRepository> getUserRepository) 
{ 
    using(IDataContext dc = getDataContext()) 
    { 
     IUserRepository repo = getUserRepository(); 
     // Do stuff with repo... 
    } 
} 
2

Tôi viết blog về vấn đề này rất gần đây:

+0

Cảm ơn các liên kết. Vì vậy, nó lại đi xuống để sử dụng một nhà máy. Và từ những gì tôi đọc, để khởi tạo nhà máy, bạn vẫn phải có một tham chiếu đến container IoC, đúng không? – TheCloudlessSky

+0

Không, bạn không cần bất kỳ loại tài liệu tham khảo để các container để có được các nhà máy, đó là toàn bộ điểm. –

+0

Ok để nhà máy có tham chiếu đến container sau đó? Nhà máy ở đâu? Tôi hiểu nhà máy được tạo ra như thế nào, tôi không biết phải đặt nó ở đâu. – TheCloudlessSky

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