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.
+3


source to share


1 answer


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.

+4


source







All Articles