Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is .NET ObservableCollection<> ToList() thread safe? If not, how to proceed

I have an ObservableCollection of Logs that I have bound to my GUI through a property

public ObservableCollection<ILog> Logs {get; private set;}

There is a requirement to show a subset of the logs somewhere else so I have:

public ObservableCollection<ILog> LogsForDisplay
    {
        get
        {
            ObservableCollection<ILog> displayLogs = new ObservableCollection<ILog>();
            foreach (Log log in Logs.ToList()) // notice the ToList()
            {
                if (log.Date != DateTime.Now.Day)
                    continue;
                displayLogs.Add(log);
            }
            return displayLogs;

        }

Before I added the "ToList()" I got exceptions occasionally about "Collection was modified; enumeration operation may not execute" Makes sense - somebody could add to Logs while I'm iterating over it. I got the idea of "ToList" from Collection was modified; enumeration operation may not execute which seems to suggest that ToList is the way to go and implies it's thread safe. But is ToList() thread safe? I assume that internally it must use the list and iterate over it? And what if someone adds to that list at the same time? Just because I haven't seen a problem doesn't mean there isn't one.

My question. Is ToList() thread safe and if not, what is the best pattern for protecting Logs? If ToList() is thread safe, do you have a reference?

Bonus question. If the requirements were to change and all I needed to display on the GUI was LogsForDisplay and NOT Logs, could I change Logs to something else that would solve the problem? Such as ImmutableList ? Then I wouldn't have to call ToList<> which I assume takes some time to make a copy.

Let me know if I can provide clarification. Thanks,

Dave

like image 317
Dave Avatar asked Mar 17 '17 17:03

Dave


1 Answers

The implementation of ToList extension method boils down to copying items from one array to another via Array.Copy method, which, while hiding Collection was modified error from you is not thread-safe and you may face weird behavior when underlying items are changed during Array.Copy calll.

What I'd suggest it to use CollectionView for binding, I've been using it for quite a long time in similar cases and faced no issues so far.

// somewhere in .ctor or other init-code
var logsForDisplay = new CollectionView(this.Logs);
logsForDisplay.Predicate = log => ((Log)log).Date == DateTime.Now.Day;

public CollectionView LogsForDisplay { get { return this.logsForDisplay; } }

You can have another CollectionView for different use case, e.g.:

// somewhere in .ctor or other init-code
var yesterdaysLogs = new CollectionView(this.Logs);
yesterdaysLogs.Predicate = log => ((Log)log).Date == DateTime.Now.AddDays(-1).Day;

public CollectionView YesterdaysLogs{ get { return this.yesterdaysLogs; } }
like image 79
alex.b Avatar answered Nov 15 '22 06:11

alex.b