Invalid Operation Exception Collection was modified C#

时间:2016-10-20 12:35:46

标签: c# selenium exception

Hello i'm trying to implement Facebook friends tracker that can track who's online and who's offline using Selenium WebDriver.

Everything works fine except when a user goes offline and i remove him from the collection it throws an exception .

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Support.Events;
using OpenQA.Selenium.Support.UI;

namespace FacebookFriendTracker
{

    public delegate void StatusChangedEventHandler(object source, StatusChangedEventArgs e);
    public class Watcher
    {
        private readonly IWebDriver _driver;
        private HashSet<User> _tempUserOnline = new HashSet<User>();
        public event StatusChangedEventHandler StatusChanged;
        private bool run;

        public Watcher()
        {
            //UsersOnline = new HashSet<User>();
            _driver = new EdgeDriver();
            _driver.Navigate().GoToUrl("https://mbasic.facebook.com");
            var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(20));
            wait.Until(ExpectedConditions.ElementExists(By.PartialLinkText("Chat")));
            var element = _driver.FindElement(By.PartialLinkText("Chat"));
            element.Click();
            run = false;
        }

        public void Tracker()
        {
            Thread.Sleep(5000);
            //_usersOnline = _driver.FindElements(By.XPath(".//a[contains(@href,'fbid')]"));

            var usersOnline = new HashSet<User>();

            foreach (var userOnline in _driver.FindElements(By.XPath(".//a[contains(@href,'fbid')]")))
            {

                var extracedAttributeValue = userOnline.GetAttribute("href");
                var regex = new Regex(@"\d+");
                var id = long.Parse(regex.Match(extracedAttributeValue).Value);
                var fullName = userOnline.Text;

                usersOnline.Add(new User() {FullName = fullName, Id = id});
            }

            while (true)
            {
                Thread.Sleep(5000);
                _driver.Navigate().Refresh();
                var newUsersOnline = new HashSet<User>();

                foreach (var user in _driver.FindElements(By.XPath(".//a[contains(@href,'fbid')]")))
                {

                    var attirbute = user.GetAttribute("href");
                    var reg = new Regex(@"\d+");
                    var newId = long.Parse(reg.Match(attirbute).Value);
                    var newFullName = user.Text;

                    newUsersOnline.Add(new User() { FullName = newFullName, Id = newId });
                }
                _tempUserOnline = usersOnline;

                foreach (var usrOnline in newUsersOnline.Except(_tempUserOnline))
                {
                    OnStatusChanged(this , new StatusChangedEventArgs() {User = usrOnline,Status = Status.Online});
                    _tempUserOnline.Add(usrOnline);
                }
                // Here it throws and exception if the the user goes offline 
                foreach (var usroffline in usersOnline.Except(newUsersOnline))
                {
                    OnStatusChanged(this, new StatusChangedEventArgs() { User = usroffline, Status = Status.Offline });
                    _tempUserOnline.Remove(usroffline);

                }

            }
        }

        protected virtual void OnStatusChanged(object source, StatusChangedEventArgs e)
        {
            if (StatusChanged !=null)
                OnStatusChanged(source,e);
            else
            {
                Console.WriteLine("User: {0} is {1} ",e.User.FullName,e.Status);
            }
        }
    }

}

2 个答案:

答案 0 :(得分:2)

You cannot modify that collection while an emueration is in progress. A common fix is to ToList() it:

foreach (var usroffline in usersOnline.Except(newUsersOnline).ToList())
{
       OnStatusChanged(this, new StatusChangedEventArgs() { User = usroffline, Status = Status.Offline });
       _tempUserOnline.Remove(usroffline);
}

答案 1 :(得分:1)

The problem is that you can't modify a collection while you're iterating through it with a foreach loop.

Here, you are setting _tempUserOnline to point to the same object as usersOnline:

_tempUserOnline = usersOnline;

Then further down, you have this loop, which modifies _tempUserOnline, thus modifying usersOnline also since they point to the same object. Since you are looping through usersOnline (and effectively _tempUserOnline too), you are getting the error when you do _tempUserOnline.Remove(usroffline).

foreach (var usroffline in usersOnline.Except(newUsersOnline))
{
    OnStatusChanged(this, new StatusChangedEventArgs() { User = usroffline, Status = Status.Offline });
    _tempUserOnline.Remove(usroffline);

}

Did you intend to copy usersOnline into tempUserOnline? If so, you should use .CopyTo() or something equivalent to make an actual copy:

User[] _tempUserOnline = new User[usersOnline.Count - 1];
usersOnline.CopyTo(_tempUserOnline);