How to wire events with methods using Autofac?
Asked Answered
L

1

8

Is it possible to wire events to methods with Autofac instead of whole object via interfaces/classes (through constructor and property injection). I want to bind at function level instead of type level. Programmatically I expect the following job to be done (in C#):

someType.Output += someOtherType.Input;

For example Spring.net does support the following construct to achieve that:

<object id="SomeType" type="Whatever.SomeType, Whatever" />
<object id="SomeOtherType" type="Whatever.SomeOtherType, Whatever">
  <listener event="Output" method="Input">
    <ref object="SomeType" />
  </listener>
</object>

Is Autofac able to do that and how? Is it possible to use xml config for such a task?

Loaded answered 28/5, 2014 at 8:4 Comment(2)
why not use classes? have you considered a third adapter class that both someType and someOtherType take a dependency on which acts as the go between for the two classes?Centipoise
Yes, possible solution but need to write extra classes just for wiring up events and subscribers. Just throught there is a simpler aproach.Loaded
F
13

I assume that you objects have no direct dependency together, like :

    public class SomeType
{
    public event EventHandler Input;

    public void Raise()
    {
        if (Input != null)
        {
            Input(this, new EventArgs());
        }
    }
}

public class SomeOtherType
{      
    public void Output(object source, EventArgs handler)
    {
        Console.WriteLine("Handled");
    }
}

You can either use Activated or bind a delegate:

Activated:

        ContainerBuilder cb = new ContainerBuilder();

        cb.RegisterType<SomeOtherType>();
        cb.RegisterType<SomeType>()
            .OnActivated(act => 
            { 
                var other = act.Context.Resolve<SomeOtherType>(); 
                act.Instance.Input += other.Output; 
            });
        var container = cb.Build();

        var obj2 = container.Resolve<SomeType>();
        obj2.Raise();

Delegate version, replace registration by:

        cb.Register(ctx =>
        {
            var other = ctx.Resolve<SomeOtherType>();
            var obj = new SomeType();
            obj.Input += other.Output;
            return obj;
        }).As<SomeType>();

As a side note, doing this type of binding can sometimes be a bit dangerous (as you create an event dependency) and create memory leak.

Creating a small class that attach both elements and implement IDisposable to unregister event when not needed anymore could be a sensible option.

I don't think it is possible to wire events via xml configuration, and for this type of binding I would largely prefer the compile time safety offered by code, but maybe you have a use case for xml.

Fayum answered 28/5, 2014 at 12:41 Comment(4)
Do you mean the example above produces a memory leak? Why? ... there is no circle or do I miss something? +1 for your input (maybe I'll take this as accepted answer in a couple of days if no better ideas are comming up)Loaded
Ok it's maybe a bit over alarming ;) You pretty much safe unless SomeType is a long lived object and SomeOtherType short lived (since now SomeType now holds a reference to SomeOtherType)Fayum
Is this also possible through xml config?Loaded
As far as I know (by looking at autoface source and documentation), it doesn't look like it's implemented via xml configuration.Fayum

© 2022 - 2024 — McMap. All rights reserved.