22

Nếu bạn không biết những gì tôi đang nói về hoặc đi qua the tutorial và cố gắng để thêm phụ thuộc Tiêm chính mình hoặc thử vận ​​may của bạn với lời giải thích của tôi về vấn đề này.Có cách nào tốt/thích hợp để giải quyết vấn đề vòng lặp tiêm phụ thuộc trong hướng dẫn ASP.NET MVC ContactsManager không?

Lưu ý: Vấn đề này không nằm trong phạm vi của hướng dẫn ban đầu trên ASP.NET. Hướng dẫn này chỉ gợi ý rằng các mẫu được sử dụng là tiêm thân thiện với phụ thuộc.

Vấn đề về cơ bản là có một vòng lặp phụ thuộc giữa Bộ điều khiển, ModelStateWrapper và ContactManagerService.

  1. Hằng số ContactController mất một IContactManagerService.
  2. Trình tạo ContactManagerService mất một IContactManagerRepository (không quan trọng) và IValidationDictionary (mà ModelStateWrapper thực hiện).
  3. Phương thức khởi tạo ModelStateWrapper lấy ModelStateDictionary (thuộc tính được gọi là "ModelState" trên bộ điều khiển).

Vì vậy, chu kỳ phụ thuộc đi như thế này: Bộ điều khiển> Dịch vụ> ModelStateWrapper> Bộ điều khiển

Nếu bạn cố gắng thêm dependency injection để này, nó sẽ thất bại. Vì vậy, câu hỏi của tôi là; Tôi nên làm gì với nó? Những người khác đã đăng câu hỏi này, nhưng câu trả lời rất ít, khác nhau, và tất cả dường như kinda "hack-ish".

giải pháp hiện tại của tôi là để loại bỏ các IModelStateWrapper từ các nhà xây dựng IService và thêm một phương pháp Initialize thay vì như thế này:

public class ContactController : Controller 
{ 
    private readonly IContactService _contactService; 

    public ContactController(IContactService contactService) 
    { 
     _contactService = contactService; 
     contactService.Initialize(new ModelStateWrapper(ModelState)); 
    } 

    //Class implementation... 
} 

public class ContactService : IContactService 
{ 
    private IValidationDictionary _validationDictionary; 
    private readonly IContactRepository _contactRepository; 

    public ContactService(IContactRepository contactRepository) 
    { 
     _contactRepository = contactRepository; 
    } 

    private void Initialize(IValidationDictionary validationDictionary) 
    { 
     if(validationDictionary == null) 
      throw new ArgumentNullException("validationDictionary"); 

     _validationDictionary = validationDictionary; 
    } 

    //Class implementation... 
} 

public class ModelStateWrapper : IValidationDictionary 
{ 
    private readonly ModelStateDictionary _modelState; 

    public ModelStateWrapper(ModelStateDictionary modelState) 
    { 
     _modelState = modelState; 
    } 

    //Class implementation... 
} 

Với cấu trúc này, tôi có thể cấu hình chứa sự hiệp nhất của tôi như thế này:

public static void ConfigureUnityContainer() 
{ 
    IUnityContainer container = new UnityContainer(); 

    // Registrations 
    container.RegisterTypeInHttpRequestLifetime<IContactRepository, EntityContactRepository>(); 
    container.RegisterTypeInHttpRequestLifetime<IContactService, ContactService>(); 

    ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container)); 
} 

Thật không may điều này có nghĩa là phương thức "Khởi tạo" trên dịch vụ phải được gọi là theo cách thủ công bởi trình tạo bộ điều khiển. Có cách nào tốt hơn? Có lẽ tôi bằng cách nào đó bao gồm IValidationDictionary trong cấu hình thống nhất của tôi? Tôi có nên chuyển sang một thùng chứa DI khác không? Tui bỏ lỡ điều gì vậy?

+0

Tôi phải thừa nhận tôi đã thực hiện hướng dẫn này 3 lần và chưa bao giờ đi qua này? Là vấn đề trong nguồn hoặc theo cách thủ công theo hướng dẫn, nếu nó là sau này có khả năng của bạn thiếu một bước? – BinaryMisfit

+0

Câu hỏi vượt ra ngoài phạm vi của hướng dẫn ban đầu mà chỉ gợi ý tại Dependency Injection nhưng không bao giờ đến xung quanh để hiển thị như thế nào nó nên được thực hiện. Tôi sẽ làm rõ câu hỏi. – JohannesH

Trả lời

11

Là một xét chung, phụ thuộc vòng tròn cho thấy một lỗ hổng thiết kế - Tôi nghĩ rằng tôi có thể yên tâm nói điều này vì bạn không phải là tác giả ban đầu của mã :)

Tôi sẽ không xem xét một phương pháp Initialize một giải pháp tốt . Trừ khi bạn đang đối phó với một kịch bản bổ trợ (mà bạn không), Phương pháp Injection không phải là giải pháp đúng. Bạn đã gần như đã tìm ra điều đó, vì bạn thấy nó không đạt yêu cầu mà bạn cần phải tự gọi nó vì DI Container của bạn không thể.

Trừ khi tôi hoàn toàn nhầm lẫn, ContactController không cần dụ IValidationDictionary trước khi các phương thức Hành động của nó đang được gọi?

Nếu điều này đúng, giải pháp đơn giản nhất có thể là xác định giao diện IValidationDictionaryFactory và làm cho hàm tạo ContactController lấy một thể hiện của giao diện này.

Giao diện này có thể được định nghĩa như thế này:

public interface IValidationDictionaryFactory 
{ 
    IValidationDictionary Create(Controller controller); 
} 

Bất kỳ phương pháp hành động trên bộ điều khiển mà cần một thể hiện IValidationDictionary sau đó có thể gọi Create phương pháp để có được những ví dụ.

Việc thực hiện mặc định sẽ giống như thế này:

public class DefaultValidationDictionaryFactory : IValidationDictionaryFactory 
{ 
    public IValidationDictionary Create(Controller controller) 
    { 
     return controller.ModelState; 
    } 
} 
+0

Tại sao lại là downvote ẩn danh? –

+6

Đánh dấu, tôi không phải là downvoter ẩn danh nhưng tôi tin rằng vấn đề ở đây là làm thế nào để có được Controller.ModelState vào trường hợp IContactService thanh lịch để được dân cư. Với giải pháp của bạn, nếu được nhận một cá thể IValidationDictionaryFactory vào cá thể IContactService, chúng ta vẫn cần một cá thể của bộ điều khiển từ bên trong dịch vụ, để gọi phương thức Create. – Ben

+0

làm thế nào để có được IValidationDictionaryFactory trong ContactService? nó phải ở trong ContactService – 1AmirJalali

1

Mỗi bộ điều khiển có một phương thức ảo Initialize để làm công cụ như thế.

Tôi nghĩ rằng không có cách nào tốt hơn bởi vì IValidationDictionary là một lớp trừu tượng giữa bạn yêu cầu hiện tại/controller/modelstate và IContactService. Tiêm bộ điều khiển mô hình hóa vào dịch vụ và sau đó tiêm dịch vụ vào bộ điều khiển chỉ đơn giản là không thể sử dụng phép xây dựng. Người ta phải là người đầu tiên.

Có thể có cách sử dụng tính năng tiêm thuộc tính? Nhưng tôi nghĩ điều này cũng sẽ phức tạp.

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