Decorators with different constructor arguments

时光怂恿深爱的人放手 提交于 2019-12-01 13:16:10

You could wrap the seed in an interface (ISeedHolder ?) and register it with a singleton lifestyle. Then use the interface in your ModelUpdatingRecorder instead of the raw int. Unless your seeds may need to be parallelized it should allow you to set the seed and resolve it when constructing the ModelUpdatingRecorder

public interface ISeedHolder
{
    int Seed {get;set;}
}

public class ModelUpdatingRecorder : IRecorder
{
    int seed;

    public ModelUpdatingRecorder(ISeedHolder seedHolder)
    {
        this.seed = seedHolder.Seed;
    }

Would this solution achieve what you need?

I've found a solution that I believe is the way this should be done. Down in the innards of Windsor the DefaultDependencyResolver has a method it uses to resolve sub-dependencies (such as a decorated instance of IRecorder above) called RebuildContextForParameter. It calls this to create a new context to use when resolving the dependency (i.e. the parameter to the constructor). The method is:

/// <summary>This method rebuild the context for the parameter type. Naive implementation.</summary>
protected virtual CreationContext RebuildContextForParameter(CreationContext current, Type parameterType)
{
    if (parameterType.ContainsGenericParameters)
    {
        return current;
    }

    return new CreationContext(parameterType, current, false);
}

The false parameter in CreationContext constructor is propagateInlineDependencies, which when true will copy over the current context's AdditionalArguments, thereby passing down the parameters to the sub-dependencies.

To flip this false to true, create a new class that derives from DefaultDependencyResolver:

public class DefaultDependencyResolverInheritContext : DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(CreationContext current, Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            return current;
        }

        return new CreationContext(parameterType, current, true);
    }
}

Then use that when creating the Windsor container:

var kernel = new DefaultKernel(
                 new DefaultDependencyResolverInheritContext(), 
                 new NotSupportedProxyFactory());
var container = new WindsorContainer(kernel, new DefaultComponentInstaller());

The NotSupportedProxyFactory and DefaultComponentInstaller are the defaults when using the parameter-less constructors for DefaultKernel and WindsorContainer.

When done, the code above will work when a factory is used to create an IRecorder, i.e.:

// during type registration/bootstrapping
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IRecorder>().ImplementedBy<NotifyingRecorder>());
container.Register(Component.For<IRecorder>().ImplementedBy<ModelUpdatingRecorder>());
container.Register(Component.For<IRecorderFactory>().AsFactory());

Where IRecorderFactory is:

public interface IRecorderFactory
{
    IRecorder Create(int seed);
}

Then this will work as expected:

IRecorderFactory recorderFactory = container.Resolve<IRecorderFactory>();
IRecorder recorder = recorderFactory.Create(20);
recorder.Add(6);

Hopefully that helps others!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!