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?

Beachwalker
  • 7,685
  • 6
  • 52
  • 94
  • 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? – wal May 28 '14 at 15:20
  • Yes, possible solution but need to write extra classes just for wiring up events and subscribers. Just throught there is a simpler aproach. – Beachwalker Aug 02 '18 at 09:16

1 Answers1

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.

mrvux
  • 8,523
  • 1
  • 27
  • 61
  • 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) – Beachwalker May 28 '14 at 15:09
  • 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) – mrvux May 28 '14 at 16:13
  • Is this also possible through xml config? – Beachwalker Jun 24 '14 at 07:05
  • As far as I know (by looking at autoface source and documentation), it doesn't look like it's implemented via xml configuration. – mrvux Jun 24 '14 at 10:24