问题
I'm currently using Castle Windsor's child container functionality to override the registration of a particular type with a specific instance in a factory method. I am using the child containers purely so that the registration is temporary for a single resolution - in other words, I don't want the registration to affect all resolutions for that type.
Perhaps some code will explain what I mean.
I have a Func which acts as a factory Func<IReportCategory, IReportCategoryViewModel>
- I give it an implementation of an IReportCategory and it returns a new instance of an IReportCategoryViewModel. (IReportCategoryViewModel has a dependency on IReportCategory).
This is registered with Castle Windsor as follows:
container.Register(
Component.For<Func<IReportCategory, IReportCategoryViewModel>>().Instance(
category => ResolveCategoryViewModelFactory(container, category)));
Where ResolveCategoryViewModelFactory
is implemented as follows:
private CategoryViewModel ResolveCategoryViewModelFactory(IWindsorContainer container, IReportCategory category)
{
using (IWindsorContainer childContainer = new WindsorContainer())
{
childContainer.Register(Component.For<IReportCategory>().Instance(category));
container.AddChildContainer(childContainer);
return childContainer.Resolve<IReportCategoryViewModel>();
}
}
What the above method achieves is the resolution of IReportCategoryViewModel, injecting the specific instance of IReportCategory as a dependency. If IReportCategoryViewModel has other dependencies that need satisfying, then these get automatically injected by the container.
I can subsequently use the Func as follows:
public class Test
{
private readonly Func<IReportCategory, IReportCategoryViewModel> factory;
public Test(Func<IReportCategory, IReportCategoryViewModel> factory)
{
this.factory = factory;
}
public void ResolveTest()
{
// Create a category (note: this would probably be resolved from the container in some way)
IReportCategory category = new ReportCategory();
// Call into the factory to resolve the view model
IReportCategoryViewModel vm = factory(category);
}
...
Question: Does this seem like a suitable thing to do? From the impression I get, child containers are not recommended in Castle Windsor - is there another way of achieving the same result?
Thanks for your help.
回答1:
Absolutely there are better ways to go, and the code you're using right now has a bug - it will try to release all the component instances you're resolving when you dispose of the child container, therefore they might be unusable (disposed) before you even get a chance to use them.
If I understand your explanation correctly it feels like a job for typed factories.
回答2:
Following Krzysztof's advice to use Typed Factories, here is how I implemented the above functionality. It is far more simpler than using child containers!
Firstly, create a factory interface which defines the signature of the factory method:
public interface ICategoryViewModelFactory
{
CategoryViewModel Create(ReportCategory category);
}
Next, ensure the TypedFactoryFacility
is enabled in the container:
container.AddFacility<TypedFactoryFacility>();
Finally, register the factory interface with the container:
container.Register(
Component.For<ICategoryViewModelFactory>()
.AsFactory());
Now you can inject ICategoryViewModelFactory
into your classes, and call the Create()
method to create a new instance of CategoryViewModel
:
public class SomeClass
{
public SomeClass(ICategoryViewModelFactory categoryViewModelFactory)
{
// This would probably be resolved by the container (it's like this for the example)
ReportCategory category = new ReportCategory();
// Get Windsor to resolve the view model using the factory
ReportCategoryViewModel vm = categoryViewModelFactory.Create(category);
....
Warning: The parameter name in the factory method needs to match the parameter name of the constructor of the object the factory creates. In the above example, the factory interface defines the method:
CategoryViewModel Create(ReportCategory category)
The constructor for CategoryViewModel
must also have the parameter named "category":
public CategoryViewModel(ReportCategory category)
This is because the factory method is the equivalent of the following:
container.Resolve<CategoryViewModel>(new { category = paramPassedIntoFactoryMethod });
来源:https://stackoverflow.com/questions/8053092/using-castle-windsor-child-containers-to-resolve-a-type-with-a-specific-instance