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