Bug 22228 - InvalidOperationException When MessagingCenter.Usubscribe Inside Message Handler
Summary: InvalidOperationException When MessagingCenter.Usubscribe Inside Message Handler
Status: RESOLVED FIXED
Alias: None
Product: Forms
Classification: Xamarin
Component: Forms ()
Version: 1.2.2
Hardware: PC Windows
: Normal normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2014-08-20 11:55 UTC by Sean Ulrich
Modified: 2015-06-01 12:17 UTC (History)
18 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Solution showing the bug (38.00 KB, application/octet-stream)
2014-12-04 06:27 UTC, Henrik Steen Møller
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Sean Ulrich 2014-08-20 11:55:50 UTC
I have discovered what I believe to be a bug with Xamarin Forms MessagingCenter.  Similar to this:
http://forums.xamarin.com/discussion/20420/messagingcenter-causing-problems-if-unsubscribing-within-message-handler

In my case I have a ViewModel (let’s call HomeViewModel) and that calls a second ViewModel with the purpose of getting some information back from it (let’s call SecondViewModel).  I want SecondViewModel to send the message and then close itself.  When HomeViewModel gets the message, it should unsubscribe to the message and then process the data.  Here is the pseudo code from HomeViewModel

        private void DoGoToSecond ()
        {
            ShowViewModel<SecondViewModel>();

            MessagingCenter.Subscribe<SecondViewModel, string>(this, “GetInfo”, (sender, info) =>
            {
                //Here is where the Exception will be thrown
                MessagingCenter.Unsubscribe<SecondViewModel, string>(this, “GetInfo”);
                DoThingsWithInfo(info);
            });
        }

Here is the code from SecondViewModel:

        private void OnInfoAquired(string info)
        {
            MessagingCenter.Send<SecondViewModel, string>(this, “GetInfo”, info);
            Close(this);
        }

When the unsubscribe executes, I get a “System.InvalidOperationException: Collection was modified; enumeration operation may not execute.”
Comment 1 Prashant manu 2014-08-26 05:47:46 UTC
Could you please provide us the test steps you followed. If possible please
attach a small complete project that demonstrates this behavior.

Also can you please add the logs from the following places?:
On XS IDE Log  Location: XS>Help> Open Log Directory> IDE.log file (with latest
timestamp)
Comment 2 Sten 2014-09-15 13:43:10 UTC
The issue is not only with unsubscribing, it's with subscribing as well.

Scenario:

- Page 1, button click sends a message. 
- There are two or more subscribers for the message
- The message causes a Navigation.PushAsync(new Page2())
- Page 2 subscribes to some message
- exception is received at the message sender, stating the collection has changed. This refers to the collection of message subscribers held internally by MessagingCenter
Comment 3 David Johnson 2014-11-11 16:22:06 UTC
The status of this bug is NEEDINFO. I'm not sure what info you need. This is really easy to reproduce, and it's giving me headaches as well. Here are the steps:

1) Subscribe to some message
2) Send that message
3) In the action from #1, add or remove a subscriber for that message

Expect: The message is sent and processed successfully, and the subscription is added or removed.

Actual: InvalidOperationException, the collection was modified...
Comment 4 edgeric 2014-11-13 21:41:52 UTC
Can we get an update on this bug?
Comment 5 Henrik Steen Møller 2014-12-04 06:16:42 UTC
I believe I've stumbled on this bug as well. This may be exactly the same David Johnson said, but I just want to make sure.

1) Subscribe in CTOR
2) When called - Unsubscribe
3) New object of same type - Subscribe in CTOR
4) When called - Unsubscribe

Expected: Works

Actual: InvalidOperationException, the collection was modified...
Comment 6 Henrik Steen Møller 2014-12-04 06:27:43 UTC
Created attachment 8962 [details]
Solution showing the bug

Solution showing the bug
Comment 7 jruskin 2014-12-09 00:38:11 UTC
Just ran into this and found a workaround if anyone still needs it.  Await a small delay (1 millisecond works for me) to give up the thread to the caller and finish enumeration of the sender list before calling Unsubscribe.

InnerSend needs to iterate over a copy of the callback list instead of the list proper which might be changed during the course of one or more of the callbacks.
Comment 8 Alec Tucker 2015-02-12 18:39:44 UTC
Same problem here. Testing on an HTC Desire I needed a 25ms delay.
Comment 9 Tomasz Kowalczyk 2015-02-19 06:45:21 UTC
For me the delay doesn't work, here is my code:

private async Task ExecuteSignInCommand()
        {
            if (IsBusy)
                return;

            IsBusy = true;

            try
            {
                User u = await _userRepository.SignIn(uDTO);

                if (u != null)
                {

                    MessagingCenter.Send<User>(u, "Authorized");
                }
}
            catch (Exception ex)
            {
                var page = new ContentPage();
                var result = page.DisplayAlert("Error", "Unable to SigIn. " + ex.Message, "OK");

                Insights.Report(ex, new Dictionary<string, string> { 
					{"SignIn_Error", "Unable to SigIn"},
					{"Message", ex.Message},
					{"Result", result.ToString()}
				});
            }

            IsBusy = false;
        }

and in subscriber:

MessagingCenter.Subscribe<User>(this, "Authorized", async (sender) =>
            {
                await System.Threading.Tasks.Task.Delay(25);

                var user = sender as User;
            };

Any thoughts?
Comment 10 Paul Read 2015-02-24 09:43:27 UTC
Just been hit by this too
Comment 11 Fokke Vermeulen 2015-03-04 11:51:54 UTC
What's with this issue. I got the same failure.
Comment 12 Kevin Kraemer 2015-03-30 12:59:40 UTC
Same issue happening for me.
Comment 13 George Cook 2015-04-02 00:19:27 UTC
same here.

in my vm I have 
		public override void OnViewDisappearing ()
		{
			base.OnViewDisappearing ();
			_isRemoved = true;
//			MessagingCenter.Unsubscribe<BasePanel> (this, "SelectedPanelContents");
		}


this method get's inovked when my vm's views are removed.

My vm receives a a selectedPanelContents message, does some stuff, and dismisses itself,w hich causes OnViewdisappearing to fire, which then causes this method.

not fun at all :/
Comment 14 Sergey Komisarchik 2015-05-31 14:58:25 UTC
Same here. Following example fails:

MessagingCenter.Subscribe<Application>(Application.Current, "Fail", _ => MessagingCenter.Unsubscribe<Application>(Application.Current, "Fail"));
MessagingCenter.Send<Application>(Application.Current, "Fail");

IMO subscription list should be immutable. Or locked at least on send. This is how events/delegates works (they are immutable objects) and MessagingCenter should be aligned with this. 

With events this works fine:

Action evt = null, handler = null;
handler = () => evt -= handler; 
evt += handler; // MessagingCenter.Subscribe(.. MessagingCenter.Unsubscribe(..)
evt.Invoke();   // MessagingCenter.Send(..)

Messenger's (MVVM light) implementation, for example, do copying subscription list before broadcasting message.

Real world example, where it works in WP, but fails in Android:

public class App : Application
{
    public App()
    {
        var navPage =
            new NavigationPage(new ContentPage()
            {
                Content = new Button()
                {
                    Text = "navigate",
                    VerticalOptions = LayoutOptions.Center,
                    HorizontalOptions = LayoutOptions.Center,
                    Command = new Command(() => MessagingCenter.Send<Application>(this, "Message"))
                }
            });
        navPage.CurrentPage.Appearing += (sender, args) => MessagingCenter.Subscribe<Application>(this, "Message", _ => navPage.PushAsync(new Page(), animated: false));
        navPage.CurrentPage.Disappearing += (sender, args) => MessagingCenter.Unsubscribe<Application>(this, "Message");

        this.MainPage = navPage;
    }
}
Comment 15 Sean Ulrich 2015-06-01 08:25:26 UTC
Info requested is provided by multiple users in comments.
Comment 16 Jason Smith [MSFT] 2015-06-01 12:17:46 UTC
This should be fixed in 1.4.3 final