Return a single element from a list in a LINQ

I'm taking elements from a list and I can't delete the elements, I have to return all of them. I found a problem at the end of this query, where it has the comment

    //Erro
 tagItems.AddElementsTo(this.TagItems);

And this error happens insert the description of the image here

Follows below the code of the entire method to be able to analyze.

public void Initialize(System.Windows.Threading.Dispatcher dispatcher)
        {
            var logbookDisposable = default(IDisposable);
            var tagsDisposable = default(IDisposable);
            var tagValuesDisposable = default(IDisposable);

            var clockDisposable = Observable.Interval(TimeSpan.FromSeconds(15))
                .StartWith(-1L)
                .SubscribeOn(dispatcher)
                .Synchronize()
                .Subscribe(_ => DateTime = DateTime.Now);

            var mainDisposable = Observable.Interval(TimeSpan.FromMinutes(15))
                .StartWith(-1L)
                .SubscribeOn(dispatcher)
                .SelectMany(async _ =>
                {
                    this.TagItems = new ObservableCollection<TagItem>();
                    var subscriptions = await GetSubscriptionsAsync();

                    return subscriptions;

                })
                .SelectMany(subs => subs)
                .Subscribe(sub =>
                {
                    var info = sub.info;
                    var tags = sub.tags;

                    logbookDisposable?.Dispose();
                    logbookDisposable = Observable.Interval(TimeSpan.FromMinutes(15))
                        .StartWith(-1L)
                        .SubscribeOn(dispatcher)
                        .Synchronize()
                        .SelectMany(async _ => await GetLogbookItems(info))
                        .SubscribeOn(dispatcher)
                        .Synchronize()
                        .Subscribe(logbookItems =>
                        {
                            this.LogbookItems = logbookItems.ToObservableCollection();
                        });

                    tagsDisposable?.Dispose();
                    tagsDisposable = Observable.Interval(TimeSpan.FromMinutes(15))
                        .StartWith(-1L)
                        .SubscribeOn(dispatcher)
                        .Synchronize()
                        .SelectMany(async _ => await GetTagItems(tags))
                        .SubscribeOn(dispatcher)
                        .Synchronize()
                        .Subscribe(tagItems =>
                        {

                            //Erro
                            tagItems.AddElementsTo(this.TagItems);


                            tagValuesDisposable?.Dispose();
                            tagValuesDisposable = Observable.Interval(TimeSpan.FromSeconds(30))
                                .SelectMany(async _ => await GetTagValues((from ti in tagItems select ti.ID).ToArray()))
                                .SubscribeOn(dispatcher)
                                .Synchronize()
                                .Subscribe(tagValues =>
                                {
                                    var query = (from ti in tagItems
                                                 join tv in tagValues on ti.ID equals tv.TagID
                                                 select new { ti, tv }).ToArray();

                                    foreach (var item in query)
                                    {
                                        item.ti.Value = item.tv.Value;
                                    }
                                });
                        });
                });

            var disposable = new Core.Utils.DisposeInvoker(() =>
            {
                clockDisposable?.Dispose();
                mainDisposable?.Dispose();
                logbookDisposable?.Dispose();
                tagsDisposable?.Dispose();
                tagValuesDisposable?.Dispose();
            });

            disposables.Add(disposable);
        }
Author: Sérgio Lopes, 2019-02-22

1 answers

What's the problem?

In multilinear(multithreaded) code collections must have data access synchronization so that calls from two or more concurrent threads do not get stuck when deciding who will first get access to the data and consequently freeze the system.

What is the solution?

The error message is informing you that the collection in question ObservableCollection<T> it does not support that a thread other than the one that created it modifies its give.

The solution is to exchange ObservableCollection<T> for a class that supports multiline calls. In the .NET Framework there is no class ObservableCollection<T> so to solve the problem you will have to create a class that will intercept the event 'OnCollectionChanged' and instead of granting the conventional change of the elements of the collection, this class it will dispatch invocations to the methods necessary for the change on fly (synchronized by the system) of the collection.

public class MyTObservableCollection<T> : ObservableCollection<T>
    {
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
            if (CollectionChanged != null)
                foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
                {
                    DispatcherObject dispObj = nh.Target as DispatcherObject;
                    if (dispObj != null)
                    {
                        Dispatcher dispatcher = dispObj.Dispatcher;
                        if (dispatcher != null && !dispatcher.CheckAccess())
                        {
                            dispatcher.BeginInvoke(
                                (Action)(() => nh.Invoke(this,
                                    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
                                DispatcherPriority.DataBind);
                            continue;
                        }
                    }
                    nh.Invoke(this, e);
                }
        }
    }

Then just change the object classes ObservableCollection<T> to MyTObservableCollection<T>.

Note: This class MyTObservableCollection<T> in global aspect it remains insecure with respect to the multilinear scope(thread unsafe) but solves its problem. If a similar error occurs in the future as another type of operation you should extend the same reasoning for such an operation. If you extend this reasoning, convert the calls from linear to on fly invocations, for the entire esta class class becomes safe with respect to the multilinear scope (thread safe).

 1
Author: Augusto Vasques, 2019-02-22 17:56:49