How can I pass a runtime parameter to a previously registered factory method using castle windsor?
I have a reporting MVC application that uses C开发者_如何学Goastle Windsor.
On application start up (in global.asax) all of the types are registered and then each subsequent request to the application resolves the relevant report type and windsor automatically handles the dependencies.
I need to switch one of the dependant types for another depending on a parameter passed in on the request.
How can I achieve this?
I have registered a factory method with windsor to handle the resolution of the switchable types but as this is registered on application start how can I pass a parameter to the factory method when the parameter is only available on a later request?
If I try registering the factory for each request it works on the first request but then complains on all subsequent requests that the factory is already registered. Unregistering the factory after each request doesnt sound like the right thing to be doing.
When you need to resolve types at runtime, the normal solution is to inject a factory that can make the decision at the appropriate time:
public class ReportFactory: IReportFactory {
IReport CreateReport(bool useDefault) {
if (useDefault) {
return new DefaultReport();
} else {
return new UnusualReport();
}
}
}
Classes that previously required an IReport
should demand an IReportFactory
instead.
You definitely don't want to go modifying your container on a per-request basis. That's a recipe for disaster.
You've got two options. The first is to use HttpContext.Current inside the factory method.
IWhatever CreateWhatever() {
if (HttpContext.Current.Request["parameter"] == "value-for-other-whatever")
return new FirstWhatever();
return DefaultWhatever();
}
The other (better) option would be to inject a factory type that resolves the correct implementation, rather than having the container do it for you.
public class WhateverFactory : IWhateverFactory {
public IWhatever GetWhatever(string parameter) {
if(parameter == "value for other whatever")
return new OtherWhatever();
return new DefaultWhatever();
}
}
Your controller would then take an instance of the factory, and would let the factory make the decision of which type to instantiate.
Use TypedFactoryFacility for your factory.
You can take two appraoches:
- Have a factory with two methods and utilise default convention it uses for locating the component:
With default convention, if you have a component registered with name "CustomFoo" the 2nd method would resolve that component, whereas the first one would get the default one.
public interface IFooFactory
{
IFoo GetFoo(...arguments);
IFoo GetCustomFoo(..arguments);
void ReleaseFoo(IFoo foo);
}
- Use custom selector and encapsulate the selection logic there.
Have come across an article on Mike Hadlow's blog that uses a delegate registered with Windsor to return one of a number of named type registrations...
So basically the Windsor registration might look something like this...
container
.Register(
Component.For<IReportFormatter>().ImplementedBy<ReportFormatterWord2003>().Named("word2003"),
Component.For<IReportFormatter>().ImplementedBy<ReportFormatterWord2007>().Named("word2007"),
Component.For<IReportFormatter>().ImplementedBy<ReportFormatterPdf>().Named("pdf"),
Component.For<Func<string, IReportFormatter>>().Instance(container.Resolve<IReportFormatter>),
Component.For<Foo>());
and the Foo constructor would take a parameter of the delegate type
Foo(Func<string, IReportFormatter> reportFormatterFactory)
now when Windsor resolves Foo, reportFormatterFactory is not resolved to an implementation of IReportFormatter instead it simply holds a reference to the container.Resolve method...
container.Resolve<IReportFormatter>(string)
that can later be used to get the correct IReportFormatter by calling...
reportFormatterFactory("word2007");
Perhaps not as easy to understand as registering a factory but does away with the need for the factory class.
精彩评论