MEF instantiating event subscribers

Apr 24, 2012 at 5:19 PM

I am using MEF for dependecy resolution but need a simplified way of creating classes so that they will handle events thrown by a class that is already composed. I do not want to inject all of these classes into the class that raises the events. Is there a way to get MEF instantiate these "listener" classes so that they can respond to events as they occur?

I am using Aggregate catalogs and assembly catalogs for composition and composite presentation events via EventAggregator. I thought about instantiating all "listeners" when I compose the container (in the "main" assembly) but was wondering if there is some other way with MEF.

Thanks

Apr 24, 2012 at 6:31 PM

Found the solution I was looking for. From this link: codebetter.com/glennblock/2010/01/15/… I now have event subscribers get started up at container composition time by having them implement IStartable and looping through all of these exports when program boots up.

Apr 25, 2012 at 11:26 AM
Edited Apr 25, 2012 at 11:27 AM

I asked a similar question on stackoverflow, though in a more general form: 

Dependency injection containers: how to handle objects that aren't a dependency of anything?

Apr 25, 2012 at 6:11 PM
Edited Apr 25, 2012 at 6:15 PM

Hi, this is the solution I came up with (from ideas suggested in the above mentioned article).

All my "listener" classes implement the IStartable interface which declares a Start() method. The implementation of which just sleeps indefinitely (I have about 4 of these "subscribers"). The event publishers are started up in a new Task (1 for each publisher). The basic bootstrapper is something like this:

 

 

        public void Start()
        {
            var startables = CompositionContainer.GetExportedValues<IStartable>();

            // Start event subscribers who are now listening. These types are not injected so as to reduce coupling.
       foreach (var startable in startables)
            {
                var task = new Task(startable.Start);
                task.Start();
            }

            // Start controller tasks (aka event publishers).
            var controllers = CompositionContainer.GetExportedValues<IController>();
            foreach (var controller in controllers)
            {
                Task.Factory.StartNew(controller.Begin);
            }
        }
After creating container, this Start() is called and it all works nicely (for my situation at least). Not sure is this is what you did, but I posted this to give others an idea if they are looking for something similar.
Mar 1, 2013 at 7:51 PM
Hello TobiasFunke!
I tried to use your trick to initiate Subscribers to events in prism, but did not work.

Could u please provide me complete code for your implementation?

Thanks.
Mar 1, 2013 at 7:59 PM
Edited Mar 1, 2013 at 8:05 PM
Hi,

Ensure your IStartable implementations are decorated with the appropriate export attribute, which in this case would be:
[Export(typeof(IStartable)]
public class Car : IStartable
{
    ...
}
This way the container will be able to import them (the IStartable's) when the container initializes.

You could also define an abstract Startable class that implements the IStartable and have all Startable (IStartable implementations) classes derive from that abstract base class which would allow your Startables to inherit the Start() method from the base class and not have to explicitly define that method for each implementation of IStartable.


It's me Tobias btw, lost my old password for that account.
Mar 2, 2013 at 7:37 AM
Hello!
Thanks for your help! But I still have some issues . Have a look at the following code:-

Interface:
[InheritedExport]
    public interface IMessageConsumer
    {
        void Start();
    }
Subscriber:
[Export(typeof(IMessageConsumer))]
    public class MessageConsumer : IMessageConsumer
    {

        [Import]
        IEventAggregator eventAggregator { get; set; }

        public void Start()
        {
            eventAggregator.
                GetEvent<CompositePresentationEvent<SharedMessage>>().
                Subscribe(OnDataReceived, true);
            //keep waiting for message 
            Thread.Sleep(-1);
        }

        // should be public!
        public void OnDataReceived(SharedMessage txt)
        {
            MessageBox.Show(txt.Message);
        }
Publisher:
[Export(typeof(PublisherView))]
    public partial class PublisherView : UserControl
    {
        [Import]
        public IEventAggregator TheEventAggregator;

        public PublisherView()
        {
            InitializeComponent();
            button1.Click += new RoutedEventHandler(button1_Click);
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            // get a reference to the event from 
            // the event aggregator
            CompositePresentationEvent<SharedMessage> myMessageEvent =
                TheEventAggregator.GetEvent<CompositePresentationEvent<SharedMessage>>();

            //Set a message to be published
            SharedMessage message = new SharedMessage
            {
                Message = "Vivane"
            };

            //publish data via event aggregator
            myMessageEvent.Publish(message);
        }
    }
Helper class:
public class SharedMessage
    {
        public string Message { get; set; }
    }
Bootstrapper:
public void Start()
        {            
            //var consumers = this._CompositionContainer.GetExportedValues<IMessageConsumer>();
            var consumers = this.Container.GetExportedValues<IMessageConsumer>();

            // Start event subscribers who are now listening. These types are not injected so as to reduce coupling.
            foreach (var consumer in consumers)
            {
                var task = new Task(consumer.Start);
                task.Start();
            }
        }

protected override void ConfigureContainer()
        {            
            base.ConfigureContainer();
            //this._CompositionContainer = new CompositionContainer(this.AggregateCatalog);
            Start();
           
        }
Issues:
  1. The MessageConsumer shows received message two times. What is the main reason?
  2. In your example U called GetExportedValues method on static CompositionContainer class. I got this error:
    "An object reference is required for the non-static field, method, or property 'System.ComponentModel.Composition.Hosting.ExportProvider.GetExportedValues<T>"
I then decided to use this.Container.GetExportedValues<> as it has been shown above. Is it ok?

I'm so sorry for asking very basic questions. Im very new to Software dev and Prism|MEF as well.

Thanks.

user233223 wrote:
Hi,

Ensure your IStartable implementations are decorated with the appropriate export attribute, which in this case would be:
[Export(typeof(IStartable)]
public class Car : IStartable
{
    ...
}
This way the container will be able to import them (the IStartable's) when the container initializes.

You could also define an abstract Startable class that implements the IStartable and have all Startable (IStartable implementations) classes derive from that abstract base class which would allow your Startables to inherit the Start() method from the base class and not have to explicitly define that method for each implementation of IStartable.


It's me Tobias btw, lost my old password for that account.
Mar 2, 2013 at 8:27 AM
Hello!
Thanks for your help! But I still have some issues . Have a look at the following code:-

Interface:
[InheritedExport]
    public interface IMessageConsumer
    {
        void Start();
    }
Subscriber:
[Export(typeof(IMessageConsumer))]
    public class MessageConsumer : IMessageConsumer
    {

        [Import]
        IEventAggregator eventAggregator { get; set; }

        public void Start()
        {
            eventAggregator.
                GetEvent<CompositePresentationEvent<SharedMessage>>().
                Subscribe(OnDataReceived, true);
            //keep waiting for message 
            Thread.Sleep(-1);
        }

        // should be public!
        public void OnDataReceived(SharedMessage txt)
        {
            MessageBox.Show(txt.Message);
        }
Publisher:
[Export(typeof(PublisherView))]
    public partial class PublisherView : UserControl
    {
        [Import]
        public IEventAggregator TheEventAggregator;

        public PublisherView()
        {
            InitializeComponent();
            button1.Click += new RoutedEventHandler(button1_Click);
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            // get a reference to the event from 
            // the event aggregator
            CompositePresentationEvent<SharedMessage> myMessageEvent =
                TheEventAggregator.GetEvent<CompositePresentationEvent<SharedMessage>>();

            //Set a message to be published
            SharedMessage message = new SharedMessage
            {
                Message = "Vivane"
            };

            //publish data via event aggregator
            myMessageEvent.Publish(message);
        }
    }
Helper class:
public class SharedMessage
    {
        public string Message { get; set; }
    }
Bootstrapper:
public void Start()
        {            
            //var consumers = this._CompositionContainer.GetExportedValues<IMessageConsumer>();
            var consumers = this.Container.GetExportedValues<IMessageConsumer>();

            // Start event subscribers who are now listening. These types are not injected so as to reduce coupling.
            foreach (var consumer in consumers)
            {
                var task = new Task(consumer.Start);
                task.Start();
            }
        }

protected override void ConfigureContainer()
        {            
            base.ConfigureContainer();
            //this._CompositionContainer = new CompositionContainer(this.AggregateCatalog);
            Start();
           
        }
Issues:
  1. The MessageConsumer shows received message two times. What is the main reason?
  2. In your example U called GetExportedValues method on static CompositionContainer class. I got this error:
    "An object reference is required for the non-static field, method, or property 'System.ComponentModel.Composition.Hosting.ExportProvider.GetExportedValues<T>"
I then decided to use this.Container.GetExportedValues<> as it has been shown above. Is it ok?

I'm so sorry for asking very basic questions. Im very new to Software dev and Prism|MEF as well.

Thanks.

user233223 wrote:
Hi,

Ensure your IStartable implementations are decorated with the appropriate export attribute, which in this case would be:
[Export(typeof(IStartable)]
public class Car : IStartable
{
    ...
}
This way the container will be able to import them (the IStartable's) when the container initializes.

You could also define an abstract Startable class that implements the IStartable and have all Startable (IStartable implementations) classes derive from that abstract base class which would allow your Startables to inherit the Start() method from the base class and not have to explicitly define that method for each implementation of IStartable.


It's me Tobias btw, lost my old password for that account.
Mar 2, 2013 at 9:25 AM
Hello!
Thanks for your help! But I still have some issues . Have a look at the following code:-

Interface:
[InheritedExport]
    public interface IMessageConsumer
    {
        void Start();
    }
Subscriber:
[Export(typeof(IMessageConsumer))]
    public class MessageConsumer : IMessageConsumer
    {

        [Import]
        IEventAggregator eventAggregator { get; set; }

        public void Start()
        {
            eventAggregator.
                GetEvent<CompositePresentationEvent<SharedMessage>>().
                Subscribe(OnDataReceived, true);
            //keep waiting for message 
            Thread.Sleep(-1);
        }

        // should be public!
        public void OnDataReceived(SharedMessage txt)
        {
            MessageBox.Show(txt.Message);
        }
Publisher:
[Export(typeof(PublisherView))]
    public partial class PublisherView : UserControl
    {
        [Import]
        public IEventAggregator TheEventAggregator;

        public PublisherView()
        {
            InitializeComponent();
            button1.Click += new RoutedEventHandler(button1_Click);
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            // get a reference to the event from 
            // the event aggregator
            CompositePresentationEvent<SharedMessage> myMessageEvent =
                TheEventAggregator.GetEvent<CompositePresentationEvent<SharedMessage>>();

            //Set a message to be published
            SharedMessage message = new SharedMessage
            {
                Message = "Vivane"
            };

            //publish data via event aggregator
            myMessageEvent.Publish(message);
        }
    }
Helper class:
public class SharedMessage
    {
        public string Message { get; set; }
    }
Bootstrapper:
public void Start()
        {            
            //var consumers = this._CompositionContainer.GetExportedValues<IMessageConsumer>();
            var consumers = this.Container.GetExportedValues<IMessageConsumer>();

            // Start event subscribers who are now listening. These types are not injected so as to reduce coupling.
            foreach (var consumer in consumers)
            {
                var task = new Task(consumer.Start);
                task.Start();
            }
        }

protected override void ConfigureContainer()
        {            
            base.ConfigureContainer();
            //this._CompositionContainer = new CompositionContainer(this.AggregateCatalog);
            Start();
           
        }
Issues:
  1. The MessageConsumer shows received message two times. What is the main reason?
  2. In your example U called GetExportedValues method on static CompositionContainer class. I got this error:
    "An object reference is required for the non-static field, method, or property 'System.ComponentModel.Composition.Hosting.ExportProvider.GetExportedValues<T>"
I then decided to use this.Container.GetExportedValues<> as it has been shown above. Is it ok?

I'm so sorry for asking very basic questions. Im very new to Software dev and Prism|MEF as well.

Thanks.

user233223 wrote:
Hi,

Ensure your IStartable implementations are decorated with the appropriate export attribute, which in this case would be:
[Export(typeof(IStartable)]
public class Car : IStartable
{
    ...
}
This way the container will be able to import them (the IStartable's) when the container initializes.

You could also define an abstract Startable class that implements the IStartable and have all Startable (IStartable implementations) classes derive from that abstract base class which would allow your Startables to inherit the Start() method from the base class and not have to explicitly define that method for each implementation of IStartable.


It's me Tobias btw, lost my old password for that account.