Return of a single element from a list to a LINQ

Asked

Viewed 49 times

0

I’m taking elements from a list and I can’t erase the elements, I have to return them all. I found a problem at the end of this query, where you have the comment

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

And this mistake happens inserir a descrição da imagem aqui

Below is the code of the whole 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);
        }

1 answer

1

What’s the matter?

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

What is the solution?

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

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

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

So just swap the object classes ObservableCollection<T> for MyTObservableCollection<T>.

Note: That class MyTObservableCollection<T> in global aspect it remains insecure with respect to 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 linear calls to on fly invocations, for the entire class this class becomes secure relative to the multilinear scope(thread safe).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.