asp.net-mvc - studio - tutorial asp.net mvc 5 español
¿Hay alguna forma correcta de solucionar el problema del lazo de inyección de dependencia en el tutorial ASP.NET MVC ContactsManager? (3)
¿Qué le parece cambiar / mejorar ligeramente el diseño a algo como esto: http://forums.asp.net/t/1486130.aspx
Si no sabes de lo que estoy hablando, revisa el tutorial e intenta agregar la inyección de dependencia tú mismo o prueba tu suerte con mi explicación del problema.
Nota: Este problema no está dentro del alcance del tutorial original en ASP.NET. El tutorial solo sugiere que los patrones utilizados son compatibles con la inyección de la dependencia.
El problema es básicamente que existe un ciclo de dependencia entre el Controlador, ModelStateWrapper y ContactManagerService.
- El constuctor ContactController toma un IContactManagerService.
- El constructor ContactManagerService toma un IContactManagerRepository (no importante) y un IValidationDictionary (que ModelStateWrapper implementa) .
- El constructor ModelStateWrapper toma un ModelStateDictionary (que es una propiedad llamada "ModelState" en el controlador) .
Entonces, el ciclo de dependencia es el siguiente: Controlador> Servicio> ModelStateWrapper> Controlador
Si intenta agregar una inyección de dependencia a esto, fallará. Entonces mi pregunta es; ¿Qué debo hacer al respecto? Otros han publicado esta pregunta, pero las respuestas son pocas, diferentes, y todas parecen un poco "hack-ish".
Mi solución actual es eliminar el IModelStateWrapper del constructor de IService y agregar un método de Inicialización como este:
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...
}
Con esta construcción puedo configurar mi contenedor de unidad de esta manera:
public static void ConfigureUnityContainer()
{
IUnityContainer container = new UnityContainer();
// Registrations
container.RegisterTypeInHttpRequestLifetime<IContactRepository, EntityContactRepository>();
container.RegisterTypeInHttpRequestLifetime<IContactService, ContactService>();
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}
Desafortunadamente, esto significa que el método de "Inicializar" en el servicio debe ser llamado manualmente por el constructor del controlador. ¿Hay una mejor manera? ¿Tal vez donde de alguna manera incluyo IValidationDictionary en mi configuración de unidad? ¿Debo cambiar a otro contenedor DI? ¿Me estoy perdiendo de algo?
Cada controlador tiene un método virtual Initialize para hacer cosas como esa.
Creo que no hay mejor manera porque el IValidationDictionary es una capa de abstracción entre su petición / controlador / modelo actual y IContactService. Inyectar controladores modelstate en el servicio y luego inyectar el servicio en el controlador es simplemente imposible usando la inyección de constructor. Uno tiene que ser el primero.
¿Puede haber una manera de usar la inyección de propiedad? Pero creo que esto también será complicado.
Como una consideración general, las dependencias circulares indican un defecto de diseño: creo que puedo decirlo con seguridad ya que no eres el autor original del código :)
No consideraría un método Initialize como una buena solución. A menos que tenga que lidiar con un escenario de complemento (que no es), la Inyección de método no es la solución correcta. Ya casi te has dado cuenta, ya que no es satisfactorio que necesites invocarlo manualmente porque tu DI Container no puede.
A menos que esté completamente equivocado, ContactController no necesita la instancia IValidationDictionary antes de invocar sus métodos de Acción.
Si esto es cierto, la solución más fácil sería definir una interfaz IValidationDictionaryFactory y hacer que el constructor ContactController tome una instancia de esta interfaz.
Esta interfaz se podría definir así:
public interface IValidationDictionaryFactory
{
IValidationDictionary Create(Controller controller);
}
Cualquier método de acción en el controlador que necesite una instancia IValidationDictionary puede invocar el método Create para obtener la instancia.
La implementación predeterminada se vería así:
public class DefaultValidationDictionaryFactory : IValidationDictionaryFactory
{
public IValidationDictionary Create(Controller controller)
{
return controller.ModelState;
}
}