5

Tengo una clase que tiene en su constructor algunos argumentos de tipo primitivo, como cadena, etc.¿Registrar un tipo con un constructor de argumentos primitivos?

¿Cómo debo registrar el tipo con el contenedor de la unidad?

public LoginManager(
    IRegionManager regionManager, 
    IEventAggregator eventAggregator, 
    string mainRegionName, 
    Uri login, 
    Uri target) 
    { 
    this.regionManager = regionManager; 
    this.eventAggregator = eventAggregator; 
    this.mainRegionName = mainRegionName; 
    this.login = login; 
    this.target = target; 
    } 
} 

actualización:
Recuerde que el IRegionManager y la IEventAggregator son conocidos tipos al prisma UnityBootstrapper que es el envoltorio de contenedores en mi caso. ¿Tengo que volver a registrarlos? Quiero mantener el tipo de registro lo más simple posible.

¿Esto se consideraría un mal hábito? ¿Alguna mejor alternativa?

Respuesta

12

Para ser sincero, trataría de evitar tener un diseño de clase que tenga tipos primitivos o difíciles de resolver en el constructor. Como ya has visto en la respuesta de Tavares, tu configuración se vuelve muy frágil (actualización: Tavares parece haber eliminado su respuesta por razones que no me quedan claras). Perderá soporte de tiempo de compilación, y cada cambio en ese constructor le haría cambiar su configuración de DI.

Existen varias formas de cambiar su diseño para evitar esto. ¿Cuál es aplicable para usted depende de usted, pero aquí están algunas ideas:

Opción 1: Utilice un DTO configuración inmutable:

private sealed class LoginManagerConfiguration 
{ 
    public Uri Login { get; private set; } 
    public Uri Target { get; private set; } 
    public string MainRegionName { get; private set; } 

    public LoginManagerConfiguration(Uri login, Uri target, string mainRegionName) 
    { 
     this.Login = login; 
     this.Target = target; 
     this.MainRegionName = mainRegionName; 
    } 
} 

Ahora usted puede dejar su LoginManager tienen una dependencia LoginManagerConfiguration:

public LoginManager(IRegionManager regionManager, 
    IEventAggregator eventAggregator, 
    LoginManagerConfiguration configuration) 
{ 
    ... 
} 

El LoginManagerConfiguration simplemente se pueden registrar como esto:

container.RegisterInstance<LoginManagerConfiguration>(
    new LoginManagerConfiguration(
     login: new Uri("Login"), 
     target: new Uri("Target"), 
     mainRegionName: ConfigurationManager.AppSettings["MainRegion"])); 

Podría ser tentador especificar un objeto de configuración de toda la aplicación en lugar de este DTO específico de tipo, pero eso es una trampa. Tal objeto de configuración de toda la aplicación es la configuración equivalente al antipatrón del localizador de servicios. No está claro qué valores de configuración requiere un tipo, y hace que las clases sean más difíciles de probar.

Opción 2: derivan de esa clase

Otra opción es derivar de esa clase, sólo con el propósito de configuración DI. Esto es especialmente útil cuando no se puede cambiar la firma de clase (es decir, cuando se trata de un componente de terceros):

private sealed class DILoginManager : LoginManager 
{ 
    DILoginManager(IRegionManager regionManager, 
     IEventAggregator eventAggregator) 
     : base(regionManager, eventAggregator, 
      ConfigurationManager.AppSettings["MainRegion"], 
      new Uri("Login"), 
      new Uri("Target")) 
    { 
     ... 
    } 
} 

definir esta clase cerca de la raíz composición de su aplicación.Esta clase se convierte en un detalle de implementación de su configuración DI. Registro de su tipo ahora será muy simple:

container.RegisterType<ILoginManager, DILoginManager>(); 

tener mucho cuidado con las llamadas que aunque los valores de configuración de carga perezosa, como ConfigurationManager.AppSettings["MainRegion"]. Esto podría llevar fácilmente a situaciones en las que los errores de configuración no se detectan durante el inicio de la aplicación, lo cual es realmente preferible.

Opción 3: Utilice un delegado de la fábrica

La última opción me gustaría presentar es una fábrica. Esto se parecerá mucho a la respuesta de Traveses, pero es más segura:

var mainRegion = ConfigurationManager.AppSettings["MainRegion"]; 

container.Register<ILoginManager>(new InjectionFactory(c => 
{ 
    return new LoginManager(
     c.Resolve<IRegionManager>(), 
     c.Resolve<IEventAggregator>(), 
     ConfigurationManager.AppSettings["MainRegion"], 
     new Uri("Login"), 
     new Uri("Target")); 
})); 

Espero que esto ayude.

+0

Para el enfoque InjectionFactory, también puede registrar un tipo Func para con usted asociar una InjectionFactory que acepta esos parámetros y devuelve un ILoginManager, así: new InjectionFactory (c => (login, target) , mainRegionName) => c.Resolver (new ParameterOverride ("mainRegionName", mainRegionName), new ParameterOverride ("login", login), new ParameterOverride ("target", target)))). Al hacer la resolución de esta manera, permaneces flexible en tu constructor para las dependencias y solo tendrías que cambiarlo por cambios de parámetros primitivos. –

+1

Muy buena respuesta, por cierto. –

+0

Olvidé agregar: el uso sería aceptar un Func como su dependencia y pasar esos parámetros a la función, lo que produciría el ILoginManager. Si se trata de configuraciones, me quedaría con el enfoque de Steven, pero si son valores de tiempo de ejecución que podrían cambiar en ciertos contextos, es posible que deba hacer algo como esto. –

0

No, no es un mal hábito. Es un escenario perfectamente válido. Han pasado algunos años desde que me mudé de Unity pero desde lo más alto de mi cabeza tienes que señalar explícitamente al constructor que deseas y enumerar todos los parámetros y para los primitivos haz new ResolvedParameter("your value").

También noté que tiene un parámetro Type allí. Tenga cuidado con Unity ya que tiene una ... muy sorprendente forma de manejarlos. Tengo un blog que detalla que here.

+0

dziękuję. Por favor, eche un vistazo a mi pregunta actualizada. Agradecería si pudieras dar un ejemplo de mi caso particular. Por cierto, ¿dónde está * la parte superior de mi cabeza *? Creo que se llama montón? – Shimmy

+0

No es necesario utilizar explícitamente el parámetro ResolvedParameter para primitivos en la gran mayoría de los casos. –

Cuestiones relacionadas