I would expect the following to produce two separate instances when using the typed factory facility.
using System;
using Castle.Facilities.TypedFactory;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(Component
.For<IFactory>()
.AsFactory()
.LifestyleSingleton());
container.Register(Component
.For<IImplementation>()
.ImplementedBy<Implementation>()
.LifestylePerThread());
var factory = container.Resolve<IFactory>();
var implementation1 = factory.Create(1);
var implementation2 = factory.Create(2);
Console.WriteLine(implementation1 == implementation2);//Returns true!
Console.Read();
}
}
public interface IFactory
{
IImplementation Create(int dependency);
}
public interface IImplementation
{}
public class Implementation : IImplementation
{
private readonly int _dependency;
public Implementation(int dependency)
{
_dependency = dependency;
}
}
}
I've also tried it with the parameter as a reference type that overrides .Equals()
and .GetHashCode()
instead of an int but it makes no difference.
I realise I can use LifestyleTransient
to solve this problem but I would actually want to receive the same instance if I pass in the same parameter.
Your expectation is incorrect.
The parameters passed to the factory method are the details that will be used to construct a new component if, and only if, there is not already a component of the required service available in the container.
Your second request is made from the same thread and is for the same service so Windsor is correctly returning the one that was already constructed.
While Gilad's suggestion is a possible one to follow, you may still find yourself 'fighting' the container and making things more complex than they need to be.
I would suggest that you embrace the mechanisms available in Windsor, which rely completely on service types (interfaces) to differentiate services from one another.
Ask yourself what it is about the two instances that is different and reflect those differences in terms of interfaces. e.g. maybe you should have an IBigImplementation
and ISmallImplementation
? The implementations of these different services can then be registered and configured in the container; you get all the pooling/reuse you intend; and consuming code remains blissfully unaware and decoupled from the implementation details.
[RANT: While factories allow for much greater flexibility, generally I regard the use of parameters to factory methods as a code smell. As you've discovered, it requires the consumer to make assumptions about the lifecycle of the service implementation. It also means that the levers and switches that control implementation details are scattered through the code base rather than all being managed centrally in container registration code.]
From what I know about Castle the TypedFactoryFacility will try and resolve for you the type according to the factory's interface. If you have a function like IImplementation Create(int dependency)
then it will try to resolve from the Kernel an object of type IImplementation
. That is why you get the same one when you have them registered with Singelton.
What you are actually looking for is a like of "TypedFactory" that will return an instance not by the type but by the instance of an object you have. What you can do is implement an ITypedFactoryComponentSelector
that will try to resolve from the kernel
and IImplementation
that also has that int dependency
you passed, and if one does not exist register one before returning it.
You can look here for deeper insights on the TypedFactory and on implementing your own ITypedFactoryComponentSelector: https://github.com/castleproject/Windsor/blob/master/docs/typed-factory-facility-interface-based.md
Hope this helps
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With