.net - tag - unity herencia
Inyección de Setter/property en Unity sin atributos (8)
Estoy trabajando en un proyecto donde el marco de Unity se utiliza como el contenedor IoC. Mi pregunta se relaciona con la inyección de una dependencia opcional (en este caso, un registrador) en varias clases mediante inyección de propiedad o de configuración.
No quiero saturar los constructores de todas mis clases con estas dependencias opcionales, pero no puedo encontrar una buena manera de manejar esto en Unity. La forma en que lo haría es, de acuerdo con la documentación de MSDN , agregando un atributo a la propiedad:
private ILogger logger;
[Dependency]
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
Encuentro esto muy feo. En StructureMap uno podría hacer lo siguiente para establecer todas las propiedades de un tipo dado:
SetAllProperties(policy => policy.OfType<ILog>());
¿Alguien sabe si es posible hacer algo similar en Unity?
Editar:
Kim Major sugiere usar este enfoque, que también se puede lograr a través del código.
Me interesarían ejemplos de cómo hacerlo automáticamente para todas las propiedades coincidentes.
El siguiente tutorial muestra una forma de hacerlo a través de la configuración. Por supuesto, también puede pasarlo por el código. http://aardvarkblogs.wordpress.com/unity-container-tutorials/10-setter-injection/
No me gustan esos atributos también
Puedes hacer todo usando el método Configure del contenedor de la unidad:
Primero registra el tipo
unityContainer.RegisterType<MyInterface,MyImpl>(
new ContainerControlledLifetimeManager());
Si tienes varios constructores tendrás que hacer esto para que Unity invoque el constructor sin parámetros (si ninguno establece Unity irá para el más gordo)
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionConstructor());
Establecer la dependencia de la propiedad
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionProperty(
"SomePropertyName",
new ResolvedParameter<MyOtherInterface>()));
Configurar la dependencia del método
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionMethod(
"SomeMethodName",
new ResolvedParameter<YetAnotherInterface>()));
Puedes intentar esto:
este código en MyClass
[InjectionMethod]
public void Initialize(
[Dependency] ILogger logger
y luego llamarlo por:
unitycontainer.BuildUp<MyClass>(new MyClass());
unity llamará entonces al método Initialize con la dependencia del contenedor y luego podrá guardarlo en una variable privada en MyClass o algo así ...
El ejemplo original que publicó parece muy engorroso, pero puede usar propiedades implementadas automáticamente como esta para ayudar a limpiar ese código:
[Dependency]
public ILogger Logger { get; set; }
Con Unity 2.1 esto funcionaría también:
var container = new UnityContainer()
.RegisterType<ILogger, Logger>()
.RegisterType<ISomeInterface, SomeImplementaion>(
new InjectionProperty("Logger", new ResolvedParameter<ILogger>()));
La propiedad inyectada de la clase SomeImplementaion es solo
public ILogger Logger { get; set; }
Tampoco me gusta mucho el uso de atributos, pero tampoco me gusta el .Configure<InjectedMembers>()
porque está vinculado a un nombre de propiedad específico y a un valor específico. La forma en que he encontrado que te da la mayor flexibilidad es crear tu propia estrategia de desarrollador.
Creé esta clase simple que itera las propiedades de un objeto que se está construyendo y establece sus valores de propiedad si el tipo de esa propiedad se ha registrado con el contenedor de unidad.
public class PropertyInjectionBuilderStrategy:BuilderStrategy
{
private readonly IUnityContainer _unityContainer;
public PropertyInjectionBuilderStrategy(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public override void PreBuildUp(IBuilderContext context)
{
if(!context.BuildKey.Type.FullName.StartsWith("Microsoft.Practices"))
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(context.BuildKey.Type);
foreach (PropertyDescriptor property in properties)
{
if(_unityContainer.IsRegistered(property.PropertyType)
&& property.GetValue(context.Existing) == null)
{
property.SetValue(context.Existing,_unityContainer.Resolve(property.PropertyType));
}
}
}
}
}
Usted registra su BuilderStrategy
creando un UnityContainerExtension
. Aquí hay un ejemplo:
public class TestAppUnityContainerExtension:UnityContainerExtension
{
protected override void Initialize()
{
Context.Strategies.Add(new PropertyInjectionBuilderStrategy(Container), UnityBuildStage.Initialization);
}
}
Eso se registra con el contenedor de Unity como tal:
IUnityContainer container = new UnityContainer();
container.AddNewExtension<TestAppUnityContainerExtension>();
Espero que esto ayude,
Matthew
Puedes inyectar tu clase con el contenedor de la unidad.
por ejemplo, si tiene una clase "MyClass" y tiene una dependencia en dos interfaces "IExample1" y "IExample2" y no quiere definir un parámetro para ellos en el constructor, entonces siga los pasos a continuación
Paso 1. Registrar ambas interfaces en el contenedor de la unidad
IUnityContainer container = new UnityContainer();
container.RegisterType<IExample1,Impl1>();
container.RegisterType<IExample2,Impl2>();
//resolve class MyClass
var myClass = container.Resolve<MyClass>();
Paso 2. Tu clase debería verse así
public class MyClass
{
IExample1 ex1;
IExample2 ex2
public MyClass(IUnityContainer container)
{
/* This unity container is same unity container that we used in step
1 to register and resolve classes. So you can use this container to resolve
all the dependencies. */
ex1= container.Resolve<IExample1>(); // will give you object of Impl1
ex2= container.Resolve<IExample2>(); // will give you object of Impl2
}
}
Paso 3 De esta forma, puede resolver cualquier cantidad de dependencias sin definirlas en el constructor.
Eche un vistazo al proyecto UnityConfiguration para la configuración basada en convenciones. Funciona bastante bien, aunque tengo un problema al buscar varias implementaciones usando ResolveAll<IType>()
. Ver esta pregunta .
container.RegisterInstance(typeof (ILogger), LoggerService.GetLogger());
container.Configure(c => c.Scan(scan =>
{
scan.AssembliesInBaseDirectory(a => a.FullName.StartsWith("My.Company")); // Filter out everthing that are not from my assemblies
scan.InternalTypes();
scan.With<SetAllPropertiesConvention>().OfType<ILogger>();
}));