EventAggregator throwning null exception when publishing event in a different namespace

Topics: Bootstrappers & IoC, Extensibility
Apr 25, 2012 at 6:40 PM

I have been able to customize the BootStrapper as you have described in your article at http://caliburnmicro.codeplex.com/wikipage?title=Customizing%20The%20Bootstrapper . I have added a second assembly that holds a number of common views and view models to be called from the main assembly by overriding the SelectAssemblies() method. This works great.

The issue I'm having is when I try to publish an event in the second assembly and handle it in my view model under the main assembly, it throws this exception:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Caliburn.Micro.EventAggregator.Publish(Object message, Action`1 marshal)

I'm passing the common view model the event aggregator from my main view model so that is not null. It seems like the event aggregator does not recognize that the event is being handled since it publishing the event from a different assembly. Is there a way to let the event aggregator know that events can be handled in one assembly when published in another assembly? Is there a way to override a BootStrapper method like we did for the assembly?

Any help would be greatly appreciated. Thanks.

Apr 26, 2012 at 10:42 PM
ramzilla007 wrote:

I have been able to customize the BootStrapper as you have described in your article at http://caliburnmicro.codeplex.com/wikipage?title=Customizing%20The%20Bootstrapper . I have added a second assembly that holds a number of common views and view models to be called from the main assembly by overriding the SelectAssemblies() method. This works great.

The issue I'm having is when I try to publish an event in the second assembly and handle it in my view model under the main assembly, it throws this exception:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Caliburn.Micro.EventAggregator.Publish(Object message, Action`1 marshal)

I'm passing the common view model the event aggregator from my main view model so that is not null. It seems like the event aggregator does not recognize that the event is being handled since it publishing the event from a different assembly. Is there a way to let the event aggregator know that events can be handled in one assembly when published in another assembly? Is there a way to override a BootStrapper method like we did for the assembly?

Any help would be greatly appreciated. Thanks.

To add more details on what we are doing:

- A bootstapper is created under a common assembly where the SelectAssemblies() method is overridden to get the current executing assembly:

 protected override IEnumerable<Assembly> SelectAssemblies()
        {
            var allAssemblies = ((dynamic)Thread.GetDomain()).GetAssemblies() as Assembly[];

            // does it bother anyone that we are dynamically loading assemblies by string name here? 
            var desiredAssemblies = allAssemblies.Where(x => x.FullName.StartsWith(AttAssemblyName)).ToList();

            desiredAssemblies.Add(Assembly.GetExecutingAssembly());
            return desiredAssemblies;
        }

- The above bootstraooer is inherited by the main bootstarraper for the client project where we populate the AggregateCatalog when overriding the configure method:

 protected override void Configure()
        {
            _container = CompositionHost.Initialize(
                new AggregateCatalog(
                    AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()
                    )
                );

   var batch = new CompositionBatch();

   IEventAggregator eventAggregator = new EventAggregator();
   IWindowManager windowManager = new WindowManager();
   eventAggregator.Subscribe(windowManager);

            //Added to allow the Generic Shellview access the Proliance User Context to do basic security checks
            IUserDataService userDataService = new UserDataService();
            //Load a ContentView/ViewModel pair for reference from the IoC
            IContentView contentView = new ContentViewModel();

   batch.AddExportedValue(eventAggregator);
   batch.AddExportedValue(windowManager);

            //Add the service so it is accessible from the IoC
            batch.AddExportedValue(userDataService);
            //Add the content view so it is accessible from IoC for the ShellViewF
            batch.AddExportedValue(contentView);

   batch.AddExportedValue(_container);

   _container.Compose(batch);
        }

- The above works great when launching view models that do not publish events. However, an exception is thrown when a view model, under the common assembly not client assembly, publishes a method that is handled by a view model in the client assembly:

public class ConfirmDialogViewModel : Screen
    {
        IEventAggregator _eventAgg;

        private string _dialogMessage = "";
        public string DialogMessage
        {
            get { return _dialogMessage; }
            set
            {
                _dialogMessage = value;
                NotifyOfPropertyChange(() => DialogMessage);
            }
        }

        public ConfirmDialogViewModel(IEventAggregator eventAgg, string msg)
        {
            _eventAgg = eventAgg;
            //_eventAgg.Subscribe(this);

            DialogMessage = msg;
            DisplayName = "PACE";
        }

        public void DoCancel()
        {
            TryClose();
        }

        public void DoConfirm()
        {
            _eventAgg.Publish(new ConfirmDialogEvent()); //Exception is thrown here!!
            TryClose();
        }
    }

- The event is under the same common assembly as the above view model; not much into it:

public class ConfirmDialogEvent
    {

    }

- The above event is handled in a view model under the client (main) assembly as so:

[Export(typeof(WirelineSelectTemplateViewModel))]
    public class WirelineSelectTemplateViewModel : Screen, IHandle<ConfirmDialogEvent> 

 {

        readonly IEventAggregator _eventAgg;
        readonly IWindowManager _windowManager;
        public WirelineSelectTemplateViewModel(IEventAggregator eventAgg, CurrentUser user)
        {
            _eventAgg = eventAgg;
            _eventAgg.Subscribe(this);
            _windowManager = IoC.Get<IWindowManager>();       

 }

public void Handle(ConfirmDialogEvent evt)
        {
            //do work here 

}

}

Let me know if you need any more details. Thank you.

Coordinator
Apr 27, 2012 at 2:10 AM

What is null?

Apr 27, 2012 at 3:10 PM
EisenbergEffect wrote:

What is null?

In the DoConfirm call above, the exception is thrown in the Publish event. The _eventAgg and ConfirmDialogEvent() are not null but I can't step into "Publish()" to see what's causing this exception:

public void DoConfirm()
        {
            _eventAgg.Publish(new ConfirmDialogEvent()); //Exception is thrown inside Publish
            TryClose();
        }

My guess is that the event aggregator is not finding a subscriber to this event and is throwing the exception even though there is a subscriber to this event in a different assembly as explained above. Thank you.