views:

488

answers:

2

I'm working on implementing an event aggregation with Prism. I have a few modules, and I want each of them to subscribe to events that tells them when they are requested. I started out doing an all plain example with both subscribed and publisher in the shell. No problems there. Now; when I move the subscribers out to my modules they don't get triggered. What's even more odd is that it actually has worked a few times - all of which I've been pending in a breakpoint. So it seems to me to be some race condition, but I don't understand why.

Assumption made: I don't need to set up the IEventAggregator anywhere - e.g. registering in the IoC container? This is built into Prism such that I only have one instance of the event aggregator, right?

So, the question is basically how/where/when I should set up my subscribers. Is there a specific order on stuff etc? In my simplified example I have one module MyModule. The Bootstrapper will add MyModule to the catalog - making it initialized:

catalog.AddModule(typeof(MyModule));

MyModule will store the aggregator and use this for subscribing to the MyModuleRequestedEvent. It also uses a menu registry to register in the application menu. The idea is that eventually clicking in the menu should trigger the event - notifying MyModule that it has been requested. Then I want it to be MyModule's responsibility to figure out what to do further.

public MyModule(IEventAggregator aggregator, IApplicationMenuRegistry menu)
{
    _applicationMenu = menu;
    _aggregator = aggregator;
}

public void Initialize()
{
    var evnt = _aggregator.GetEvent<MyModuleRequestedEvent>();
    evnt.Subscribe(MyModuleRequested);
    _applicationMenu.RegisterMenuItem("MyModule", evnt);
}

public void MyModuleRequested(bool b)
{
    MessageBox.Show("MyModule requested");
}

Now, I have a button in my shell which will publish this event. The shell gets the same (?) event aggregator when resolved.

public Shell(IEventAggregator aggregator)
{
    InitializeComponent();
    var evnt = aggregator.GetEvent<MyModuleRequestedEvent>();
    EventTriggerButton.Click += (s, e) => evnt.Publish(true);
}

Notes:

  • Have verified that the event is published. Adding a subscriber in the shell too will make this subscriber receive the event.
  • Again; the subscriber in MyModule isn't triggered. However, it has - strangely - been on a few occasions.
  • I don't use the input to the event. It seemed like you needed to have some input-type, so I just went with a dummy bool. Can I get rid of this..?
+1  A: 

So, I just got a theory, but no time to test it right now.. Will do tomorrow.

Question: Will adding modules to the ModuleCatalogue keep them alive? I assumed it would. Hence - the MyModule should stay alive - and then will be triggered when the event is published.

protected override IModuleCatalog GetModuleCatalog()
{
    var catalog = new ModuleCatalog();
    catalog.AddModule(typeof(MyModule));
    return catalog;
}

However, if this doesn't keep the module alive it is obvious that it will have a hard time respond to the event. The module-object dies, but it doesn't unsubscribe - hence I'll see the subscriber in the EventAggregator list, but the subscriber isn't around anymore. Also; I mentioned that I it does in fact work occasionally - which would be the case if the garbage collector didn't have time to take out the trash before the event is triggered.

Does this sound like the case? If so - I haven't thought of a solution yet so you're welcome to suggest one in a different reply-thread..

So; what is the ModuleCataloge anyway? Just a list kept for Initialization and then thrown away?

stiank81
+3  A: 

The Prism Event Aggregator uses Weak References to link to the events. This is to prevent memory leaks from event handlers.

Once a module initializer has been run it's disposed of, so your event handler is being destroyed before the event is being fired. You can tell Prism to keep the event handler around by using an overload of Subscribe.

evnt.Subscribe(MyModuleRequested, true);

As a pattern, I tend to put any event subscribers in a separate class, and call that class from the modules Initialize method. That way the events stay alive but separate while the module is still destroyed.

Cameron MacFarland
Perfect! And your pattern makes sense - so I will put a minimal event handler in a separate class. Thanks..!
stiank81