Gibt es eine gute/richtige Weg zur Lösung des dependency injection-loop-problem in der ASP.NET MVC ContactsManager tutorial?
Wenn Sie nicht wissen, was ich Rede, gehen Sie entweder durch das tutorial und versuchen add dependency Injection selbst oder versuchen Sie Ihr Glück mit meiner Erklärung des Problems.
Hinweis: Dieses problem nicht im Rahmen des ursprünglichen Tutorials auf ASP.NET. Die Anleitung wird nur suggeriert, dass die Muster, die verwendet werden, sind dependency injection freundlich.
Das problem ist im Grunde, dass es eine Abhängigkeit Schleife zwischen der Steuerung, der ModelStateWrapper und die ContactManagerService.
- Die ContactController constuctor nimmt eine IContactManagerService.
- Die ContactManagerService Konstruktor nimmt eine IContactManagerRepository (nicht wichtig) und ein IValidationDictionary (die ModelStateWrapper implementiert).
- Die ModelStateWrapper Konstruktor nimmt einen ModelStateDictionary (das ist eine Eigenschaft, die man als "ModelState" auf dem controller).
Also die Abhängigkeit Zyklus geht wie folgt: Controller - > Service > ModelStateWrapper > Controller
Wenn Sie versuchen, hinzufügen von dependency injection auf diese, wird es scheitern. Also meine Frage ist; was soll ich dagegen tun? Andere haben geschrieben, diese Frage, aber die Antworten sind einige, verschiedene, und alle scheinen ein bisschen "hack-ish".
Meine aktuelle Lösung ist das entfernen der IModelStateWrapper aus dem IService Konstruktor und fügen Sie eine Initialize-Methode statt wie diese:
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 kann ich bei der Konfiguration meines unity-container wie diese:
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 auf dem service aufgerufen werden manuell von dem controller-Konstruktor. Gibt es einen besseren Weg? Vielleicht, wo ich irgendwie gehören die IValidationDictionary in meiner Einheit-Konfiguration? Sollte ich zu einem anderen wechseln DI-container? Bin ich etwas fehlt?
- Ich muss zugeben, dass ich dies getan haben tutorial 3-mal und noch nie über diesen? Ist das problem in der Quelle oder manuell, nach dem tutorial, wenn es das Letzte ist Ihr wahrscheinlich fehlt ein Schritt?
- Die Frage geht über den Rahmen des ursprünglichen Tutorials, die nur angedeutet Dependency Injection aber nie kam, um zu zeigen, wie es getan werden sollte. Ich werde das klären der Frage.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Als Allgemeine überlegung, zirkuläre Abhängigkeiten deuten auf einen Konstruktionsfehler - ich denke, ich kann sicher sagen, dass diese, da Sie nicht der ursprüngliche Autor des Codes 🙂
Ich würde das nicht als eine Initialize-Methode eine gute Lösung. Es sei denn, Sie sind den Umgang mit einem add-in Szenario (was Sie nicht sind), die Methode der Injektion ist nicht die richtige Lösung. Sie haben fast schon herausgefunden, da Sie es unbefriedigend finden, dass Sie brauchen, um manuell aufrufen, da der DI-Container nicht.
Wenn ich mich nicht völlig Irre, der ContactController muss nicht die IValidationDictionary Instanz vor seiner Action-Methoden werden aufgerufen?
Wenn das wahr ist, ist die einfachste Lösung wäre wahrscheinlich, zu definieren, eine IValidationDictionaryFactory-Schnittstelle und stellen Sie die ContactController Konstruktor eine Instanz dieser Schnittstelle.
Diese Schnittstelle kann definiert werden, wie diese:
Jede Action-Methode auf dem controller, braucht eine IValidationDictionary Instanz können dann rufen Sie die Create-Methode auf, um die Instanz.
Die default-Implementierung würde so Aussehen:
Wie über etwas ändern/verbessern, das design zu etwas wie diesem: http://forums.asp.net/t/1486130.aspx
Jeder controller hat eine virtuelle Methode Initialisieren zu tun Sachen wie, dass.
Ich denke, es gibt keinen besseren Weg, weil die IValidationDictionary ist eine Abstraktionsschicht zwischen Sie aktuelle Anfrage/controller/modelstate und die IContactService. Die Injektion Controller modelstate in den Dienst und dann die Injektion der service in den controller ist einfach unmöglich mit constructor injection. Man muss der erste sein.
Vielleicht gibt es eine Möglichkeit mithilfe von property-injection? Aber ich denke, das wird zu kompliziert.