问题
I am using Castle Windsor 2.5.1 in an ASP.NET MVC project and using property injection to create an object which I expect to always be available on a base controller class. I am using a factory to create this object, however if there is an error in the constructor, I do not get a warning from Windsor at all and it just returns my object but without injecting the property.
Is this the expected behaviour, and if so, how can I get an error raised when a factory fails to return anything?
Here is an example
public class MyDependency : IMyDependency
{
public MyDependency(bool error)
{
if (error) throw new Exception("I error on creation");
}
}
public interface IMyDependency
{
}
public class MyConsumer
{
public IMyDependency MyDependency { get; set; }
}
[TestFixture]
public class ProgramTest
{
[Test]
public void CreateWithoutError() //Works as expected
{
var container = new WindsorContainer().Register(
Component.For<IMyDependency>().UsingFactoryMethod(() => new MyDependency(false)).LifeStyle.Transient,
Component.For<MyConsumer>().LifeStyle.Transient
);
var consumer = container.Resolve<MyConsumer>();
Assert.IsNotNull(consumer);
Assert.IsNotNull(consumer.MyDependency);
}
[Test]
public void CreateWithError_WhatShouldHappen() //I would expect an error since it can't create MyDependency
{
var container = new WindsorContainer().Register(
Component.For<IMyDependency>().UsingFactoryMethod(() => new MyDependency(true)).LifeStyle.Transient,
Component.For<MyConsumer>().LifeStyle.Transient
);
Assert.Throws<Exception>(() => container.Resolve<MyConsumer>());
}
[Test]
public void CreateWithError_WhatActuallyHappens() //Gives me back a consumer, but ignores MyDependency
{
var container = new WindsorContainer().Register(
Component.For<IMyDependency>().UsingFactoryMethod(() => new MyDependency(true)).LifeStyle.Transient,
Component.For<MyConsumer>().LifeStyle.Transient
);
var consumer = container.Resolve<MyConsumer>();
Assert.IsNotNull(consumer);
Assert.IsNull(consumer.MyDependency); //Basically fails silently!
}
}
An interesting observation, if I use this in my MVC application, I get an internal error from Windsor when calling ReleaseComponent
-- so even though it did not give me back a class with my dependency injected, it still appears to try releasing it.
回答1:
As far as I know, yes, that's the intended behavior. This isn't specific to factory methods, it works like that for all optional service dependencies. Optional dependencies that throw when resolving are treated as non-resolvable. This is defined in DefaultComponentActivator.ObtainPropertyValue()
Of course, you could always override the default activator with your own if you want to change this behavior.
回答2:
As well as the option suggested by Mauricio, it is also possible to create a Facility to achieve the expected behaviour as explained on this example page about Facilities.
Here is my implementation which is slightly more concise:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class NonOptionalAttribute : Attribute
{
}
public class NonOptionalPropertiesFacility : AbstractFacility
{
protected override void Init()
{
Kernel.ComponentModelBuilder.AddContributor(new NonOptionalInspector());
}
}
public class NonOptionalInspector : IContributeComponentModelConstruction
{
public void ProcessModel(IKernel kernel, ComponentModel model)
{
foreach (var prop in model.Properties.Where(prop => prop.Property.IsDefined(typeof (NonOptionalAttribute), false)))
{
prop.Dependency.IsOptional = false;
}
}
}
Then just decorate any properties with [NonOptional]
and you will receive an error if there is an issue with construction.
回答3:
As of Castle Windsor 3.2 there's new a cool addition that is Diagnostic logging in the container.
So if you do this for example in an ASP.NET MVC app:
var logger = _container.Resolve<ILogger>();
((IKernelInternal)_container.Kernel).Logger = logger;
you can redirect the logs caught by Windsor to your configured log4net logger.
The type of information currently being logged includes:
- When Windsor tries to resolve an optional dependency (like property injection), but fails due to an exception, the exception is logged.
- When registering a type by convention and ignoring it due to an existing registration for that type, this fact is logged.
来源:https://stackoverflow.com/questions/4398101/castle-windsor-strange-behaviour-wth-property-injection-and-factory-method