Tuesday, March 2, 2010

Silverlight 4 with MEF - System.ComponentModel.Composition.ChangeRejectedException

I’m adapting one of the bright creations of Jeremy Likness on

MEF instead of PRISM for Silverlight 3 Part 1 of 2: Dynamic Module Loading
MEF instead of PRISM for Silverlight 3 Part 2 of 2: Region Management

for my needs and ran into the following exception during the call to CompositionInitializer.SatisfyImports:

System.InvalidOperationException: The package downloaded successfully but an error occurred while reading the contents of the package. See the inner exception for more details. ---> System.ComponentModel.Composition.ChangeRejectedException: The composition remains unchanged. The changes were rejected because of the following error(s): The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) Change in exports prevented by non-recomposable import 'Advaton.EventViewer.Silverlight.Shell.ShellViewModel.Manager (ContractName="Advaton.Silverlight.Regions.RegionManager")' on part 'Advaton.EventViewer.Silverlight.Shell.ShellViewModel'.

Solution to this particular issue is at the bottom of the post. Here I wanted to summarize what was required in order to see the exception on the shell:

Adding event to INavigation to raise when navigation error happens:

        /// <summary>
        /// Raised when error happens during the navigation attempt
        /// </summary>
        event EventHandler<NavigationErrorEventArgs> NavigationErrorRaised;

Adding handling for the load completion to ViewNavigator class:

………..
catalog.DownloadCompleted += new System.EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(catalog_DownloadCompleted);

                catalog.DownloadAsync();
                _downloadedModules.Add(_viewMap[view]);
            }
        }
 
        void catalog_DownloadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                OnNavigationError(e.Error);
            }
        }
 
 
        public event System.EventHandler<NavigationErrorEventArgs> NavigationErrorRaised;
 
        private void OnNavigationError(Exception ex)
        {
            if (NavigationErrorRaised != null)
            {
                NavigationErrorRaised(this, new NavigationErrorEventArgs { Error = ex });
            }
        }

Making ShellViewModel to implement INotifyPropertyChanged and hooking into injection of INavigation Navigation to get connected to the Error during load/composition:

 

    [Export]
    public class ShellViewModel : INotifyPropertyChanged
    {
        private INavigation navigation;
 
        [Import]
        public INavigation Navigation
        {
            get { return navigation; }
            set
            {
                navigation = value;
                navigation.NavigationErrorRaised += new System.EventHandler<NavigationErrorEventArgs>(navigation_NavigationError);
            }
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        private void OnNotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
 
        private string error;
        /// <summary>
        /// Error to show on the shell.
        /// </summary>
        public string Error
        {
            get { return error; }
            set
            {
                error = value;
                OnNotifyPropertyChanged("Error");
            }
        }

Then giving Error some space on the Shell control:

<TextBox Text="{Binding Path=Error}" Grid.Row="3" Foreground="Red" IsReadOnly="True" TextWrapping="Wrap" />  

To achieve the message on shell for the navigation/load/composition errors:

image

Cool. Back to the post subject!

The issue was with me setting a reference with Copy Local as True in dynamically loaded control project to one of the assemblies referenced already by the shell project. So once the property got injected in the main xap, after loading another xap dynamically it tried to recompose the property again with dll from the dynamic xap. And in my case this is by no chance about AllowRecomposition but about setting the Copy Local to false for such references in xap projects.

image

There are other scenarios when this exception happens – it can be good idea to search stackoverflow for it: http://stackoverflow.com/search?q=ChangeRejectedException

2 comments:

Anonymous said...

Thanks for this solution. I am using Jeremy's Jounce framework and ran in to this Exception. Changing Copy Local to false for the dynamically loaded module fixed my problem.
Thanks.

Anonymous said...

Thanks a lot for this post! The "copy local" solution was exactly what I needed