D.AhmedRafik D.AhmedRafik - 1 month ago 5
C# Question

Invalid Operation Exception Collection was modified C#

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);
}
}
}

}

Answer

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);
}