22

Wenn Sie nicht wissen, wovon ich spreche, gehen Sie entweder the tutorial durch und versuchen Sie, Abhängigkeit hinzuzufügen. Spritzen Sie sich ein oder versuchen Sie Ihr Glück mit meiner Erklärung des Problems.Gibt es eine gute/richtige Methode zum Lösen des Abhängigkeitsinjektionsschleife-Problems im ASP.NET MVC ContactsManager-Lernprogramm?

Hinweis: Dieses Problem liegt nicht im Rahmen des ursprünglichen Lernprogramms zu ASP.NET. Das Lernprogramm schlägt nur vor, dass die verwendeten Muster für die Abhängigkeitsinjektion geeignet sind.

Das Problem besteht im Wesentlichen darin, dass eine Abhängigkeitsschleife zwischen dem Controller, dem ModelStateWrapper und dem ContactManagerService besteht.

  1. Der ContactController-Konstruktor übernimmt einen IContactManagerService.
  2. ContactManagerService Der Konstruktor eine IContactManagerRepository (nicht wichtig) und eine IValidationDictionary (die ModelStateWrapper Arbeitsgeräte).
  3. Der ModelStateWrapper-Konstruktor verwendet ein ModelStateDictionary (eine Eigenschaft namens "ModelState" auf dem Controller).

So ist die Abhängigkeit Zyklus geht so: Controller> Service> ModelStateWrapper> Controller

Wenn Sie versuchen, Dependency Injection zu dieser hinzuzufügen, wird es scheitern. Also meine Frage ist; was soll ich deswegen machen? Andere haben diese Frage gestellt, aber die Antworten sind wenige, andere, und alle scheinen irgendwie "hack-ish".

Meine aktuelle Lösung ist den IModelStateWrapper vom IService Konstruktor und füge eine Initialize-Methode stattdessen wie folgt zu entfernen:

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... 
} 

Mit diesem Konstrukt ich meine Einheit-Container wie folgt konfigurieren:

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

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

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

Leider bedeutet dies, dass die "Initialize" -Methode für den Dienst vom Controller-Konstruktor manuell aufgerufen werden muss. Gibt es einen besseren Weg? Vielleicht wo ich das IValidationDictionary irgendwie in meine Einheitskonfiguration einfüge? Soll ich zu einem anderen DI-Container wechseln? Fehle ich etwas?

+0

Ich muss zugeben, dass ich dieses Tutorial 3 mal getan haben und nicht immer ganz so weit kommen? Ist das Problem in der Quelle oder manuell nach dem Tutorial, wenn es das letztere ist, vermissen Sie wahrscheinlich einen Schritt? – BinaryMisfit

+0

Die Frage geht über den Umfang des ursprünglichen Tutorials hinaus, das nur auf Dependency Injection hindeutete, aber nie dazu kam, zu zeigen, wie es gemacht werden sollte. Ich werde die Frage klären. – JohannesH

Antwort

11

Als allgemeine Überlegung, zirkuläre Abhängigkeiten zeigen einen Designfehler - ich glaube, ich sicher sagen kann, da Sie nicht der ursprüngliche Autor des Codes sind :)

Ich würde eine Initialize-Methode eine gute Lösung nicht berücksichtigen . Wenn Sie nicht mit einem Add-In-Szenario arbeiten (was Sie nicht sind), ist Method Injection nicht die richtige Lösung. Sie haben dies fast schon herausgefunden, da Sie es nicht zufriedenstellend finden, dass Sie es manuell aufrufen müssen, weil Ihr DI-Container das nicht kann.

Wenn ich mich nicht völlig irre, benötigt der ContactController die IValidationDictionary-Instanz nicht, bevor die Action-Methoden aufgerufen werden.

Wenn dies zutrifft, wäre wahrscheinlich die einfachste Lösung, eine IValidationDictionaryFactory-Schnittstelle zu definieren und den ContactController-Konstruktor dazu zu bringen, eine Instanz dieser Schnittstelle zu übernehmen.auf dem Controller

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

Jede Aktion Methode, die dann die die Instanz erhalten Create-Methode aufrufen kann eine IValidationDictionary Instanz benötigt:

Diese Schnittstelle könnte wie folgt definiert werden.

Die Default-Implementierung würde wie folgt aussehen:

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

Warum der anonyme Downvote? –

+6

Mark, ich bin nicht der anonyme Downvoter, aber ich glaube, das Problem hier ist, wie man den Controller.ModelState elegant in die IContactService-Instanz bringt, um ihn zu füllen. Wenn Ihre Lösung eine IValidationDictionaryFactory-Instanz in die IContactService-Instanz importieren würde, benötigen wir mit Ihrer Lösung weiterhin eine Instanz des Controllers aus dem Service, um die Create-Methode aufzurufen. – Ben

+0

Wie bekomme ich IValidationDictionaryFactory in ContactService? Es sollte in ContactService sein – 1AmirJalali

1

Jeder Controller verfügt über eine virtuelle Methode initialisieren Sachen wie das zu tun.

Ich denke, es gibt keinen besseren Weg, weil das IValidationDictionary eine Abstraktionsschicht zwischen Ihnen aktuelle Anfrage/Controller/Modelstate und der IContactService ist. Das Einfügen von Controllern in den Dienst und das anschließende Einfügen des Dienstes in den Controller ist mit der Konstruktorinjektion einfach unmöglich. Einer muss der Erste sein.

Kann es sein, dass es eine Möglichkeit gibt, die Eigenschaft zu injizieren? Aber ich denke, das wird auch kompliziert.

Verwandte Themen