Argument-related lifestyle at Windsor Castle
I am allowing the IoC container to manage the life of my artwork and seems to be reusing a special lifestyle that is not available in the Windsor castle by default set to .
Example
I need to construct objects in context, specifically by passing time-resolution arguments to a factory, and when I, for example, pass a Dorothy string, I would like to return a Person singleton to be created with Dorothy's string if it doesn't already exist in the container , or an existing instance of Dorothy. Same thing, passing Jane's string argument to my factory - please return Person Jane's singleton. And there could be Sally, Bob, and many others, a complete set of inputs decaying only at runtime.
Manual implementation
Here's another example implemented using a dictionary in my custom class:
private readonly static IDictionary<string,IRecruiter> Recruiters
= new Dictionary<string, IRecruiter>();
private IRecruiter GetRecruiter(string recruiterId)
{
IRecruiter recruiter;
if (!Recruiters.TryGetValue(recruiterId, out recruiter))
{
recruiter = this.donorFactory.CreateRecruiter(recruiterId);
Recruiters.Add(recruiterId,recruiter);
}
return recruiter;
}
And where is donorFactory's CreateRecruiter method doing something like this:
return this.Create<IRecruiter>(new Arguments { { typeof(string), recruiterId }});
SRP and Boiler Room Reduction
I find that I use this pattern a lot, so I would like to know if it is possible to implement it using the existing Castle Windsor styles, or maybe there is a case to add this pattern to Castle Windsor itself?
- Registering components as .Named does not work in this case because the set of incoming arguments is unknown at design time
- A template can have a generic implementation that will work on any type of argument (not just a string), or even on sets of arguments if GetHashCode or something like that was used as a key for a dictionary.
source to share
I can see one way to do this by creating a special access aspect:
public class ArgScopeAccessor : IScopeAccessor
{
static readonly ConcurrentDictionary<string, ILifetimeScope> collection = new ConcurrentDictionary<string, ILifetimeScope>();
public void Dispose()
{
foreach (var scope in collection)
scope.Value.Dispose();
collection.Clear();
}
public ILifetimeScope GetScope(CreationContext context)
{
string name = (string)context.AdditionalArguments["name"];
return collection.GetOrAdd(name, n => new DefaultLifetimeScope());
}
}
This creates a scope based on the argument name
passed to the call to resolve the object. Then I used a typed factory object to create IRecruiterFactory
(with a parameter named name
):
public interface IRecruiterFactory
{
IRecruiter Create(string name);
}
And suppose yours IRecruiter
and Recruiter
look something like this:
public interface IRecruiter
{
string Name { get; }
}
public class Recruiter : IRecruiter
{
public Recruiter(string name)
{
Name = name;
}
public string Name { get; private set; }
}
Then you can configure your container to use this scope reusable like this:
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IRecruiterFactory>()
.AsFactory());
container.Register(Component.For<IRecruiter>()
.ImplementedBy<Recruiter>()
.LifestyleScoped<ArgScopeAccessor>());
So now the recruiter's solution by name will resolve the same instance:
IRecruiterFactory recruiterFactory = container.Resolve<IRecruiterFactory>();
IRecruiter jane1 = recruiterFactory.Create("Jane");
IRecruiter susan = recruiterFactory.Create("Susan");
IRecruiter jane2 = recruiterFactory.Create("Jane");
Console.WriteLine("Jane 1: " + jane1.GetHashCode());
Console.WriteLine("Jane 2: " + jane2.GetHashCode());
Console.WriteLine("Susan: " + susan.GetHashCode());
Output:
Jane 1: 60467532 Jane 2: 60467532 Susan: 63249743
Obviously, you can create another accessory to access any other ways you want to differentiate by type, like a hash code or a combination of arguments. However, I don't see a way to change the constant name
in ArgScopeAccessor
as there is no way to pass a constructor argument when setting up a scope accessor in a container. But this can be solved with base type and derived types just by specifying a constant.
source to share