温莎城堡的议论生活方式

时间:2014-09-29 09:22:22

标签: c# .net dependency-injection instance castle-windsor

我让我的IoC容器管理我的对象图的生命,并且似乎正在重复使用目前无法使用的特定生活方式in the Castle Windsor default set

实施例

我需要通过将解析时参数传递到工厂来构造对象上下文,例如,当我传入字符串Dorothy时,我想找回一个将用字符串Dorothy实例化的Person的单例,如果容器中还没有,或者现有的Dorothy实例。将字符串参数Jane传递给我的工厂时也一样 - 请返回Person Jane的单例。并且可能有Sally,Bob和许多其他人,仅在运行时解开的完整输入集。

手动实施

这是另一个使用自定义类中的字典实现的示例:

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;
    }

并且donorFactory CreateRecruiter方法执行类似的操作:

return this.Create<IRecruiter>(new Arguments { { typeof(string), recruiterId }});

SRP和减少锅炉

我发现我经常使用这种模式,所以想知道是否可以使用现有的Castle Windsor生活方式来实现,或者可能有一个案例要将此模式添加到Castle Windsor本身?

  • 将组件注册为.Named在这种情况下不起作用,因为在设计时未知传入参数集
  • 该模式可以有一个通用的实现,可以使用任何类型的参数(不只是字符串)甚至是参数集,如果GetHashCode或类似的东西被用作字典的键。

1 个答案:

答案 0 :(得分:5)

我可以通过创建自定义范围访问器来看到一种方法:

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());
    }
}

这会根据传递给调用的name参数创建一个scope来解析对象。然后,我使用typed factory facility创建IRecruiterFactory(名为name的参数):

public interface IRecruiterFactory
{
    IRecruiter Create(string name);
}

假设您的IRecruiterRecruiter是这样的:

public interface IRecruiter
{
    string Name { get; }
}

public class Recruiter : IRecruiter
{
    public Recruiter(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
}

然后,您可以设置容器以可重用的方式使用此范围:

container.AddFacility<TypedFactoryFacility>();

container.Register(Component.For<IRecruiterFactory>()
                            .AsFactory());
container.Register(Component.For<IRecruiter>()
                            .ImplementedBy<Recruiter>()
                            .LifestyleScoped<ArgScopeAccessor>());

现在,按名称解析招聘人员将解决同一个实例:

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());

显示器:

Jane 1: 60467532
Jane 2: 60467532
Susan:  63249743

显然,您可以创建一个不同的范围访问器来解决您希望区分类型的任何其他方式,例如哈希代码或参数组合。但是,我没有看到更改nameArgScopeAccessor常量的方法,因为在配置容器上的范围访问器时无法传递构造函数参数。但这可以通过基类型和派生类型来解决,只需指定常量。