Is it possible to obtain instance of component being resolved before satisfying property dependency?

StackOverflow https://stackoverflow.com/questions/13770336

  •  05-12-2021
  •  | 
  •  

Pregunta

public class A
{
    public X x { get; set; }
}

public class B
{
    public X x { get; set; }
}

public class X
{
    public X(object owner) { /* ... */ }
}

Basically if classes A and B are registered in Windsor I want to be able to resolve X dependency in such a way that it gets instance of the class it was required for.

In plain code it would look like that:

var a = new A();
var x = new X(a);
a.X = x;

Is there a way to do this in Windsor, maybe through some extensibility mechanism?


It looks like some crazy question even for me, so here is some motivation behind it:

The X in the example above is ITracer which is a proxy for TraceSource that is adding some bits of info to each traced message, namely, unique ID of the owner and its type (now it is only ID - that's why the question - I can't get to the instance and call GetType() on it).

A brief example to make it more clear. Suppose you have some service IService and want to add traces to it in the most non-intrusive way. But in the application could be dozen of instances of this service, so in traces you want to distinguish them by ID/type. It would be good if class received its tracer from the container and just wrote messages there when needed, without thinking of IDs, concrete TraceSource etc.

I already have some infrastructure that allows me to write like that:

[TracedTo("NameOfTheTraceSource")]
public class Service : IService
{
    public ITracer Tracer { get; set; }
}

And Windsor correctly resolves Tracer to be its own (non-shared with other objects) instance of ITracer pointing to TraceSource with name NameOfTheTraceSource. Moreover, if I add traceAllMethods = true to the attribute - Windsor will automatically add interceptor which will write down each method call on this instance via the same Tracer (and only does this if corresponding TraceSource has some listeners configured - we don't have to support adding them on the fly). This is just awesome because it doesn't require anything from the developer of Service and it doesn't suffer performance degradation when it is not needed, not a bit. And so I'm working to make this even more convenient :)

¿Fue útil?

Solución

OK, I think what you want here is a facility. Here's a simple one (with no error checking) that may be what you're looking for (or at least point you in the right direction):

public class XFacility : AbstractFacility
{
    protected override void Init()
    {
        this.Kernel.ComponentCreated += KernelOnComponentCreated;
    }

    private void KernelOnComponentCreated(ComponentModel model, object instance)
    {
        var props =
            instance.GetType().GetProperties().Where(p => p.CanWrite && p.PropertyType == typeof (X));
        if (props.Any())
        {
            var pi = props.First();
            pi.SetValue(instance, new X(instance), null);
        }
    }
}

Now make sure you add the facility to the container before you do any resolving:

var container = new WindsorContainer();
container.AddFacility<XFacility>();
container.Register(Component.For<A>(),
                    Component.For<B>()
    );

var a = container.Resolve<A>();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top