Я изучал использование Rx в среде MVVM. Идея состоит в том, чтобы использовать «живые» запросы LINQ по наборам данных в памяти, чтобы проецировать данные в модели представления для привязки.
Ранее это было возможно с использованием INotifyPropertyChanged / INotifyCollectionChanged и библиотеки с открытым исходным кодом с именем CLINQ . Потенциал использования Rx и IObservable состоит в том, чтобы перейти к гораздо более декларативной ViewModel, используя классы Subject для распространения измененных событий из исходной модели через View. Для последнего шага потребуется преобразование из IObservable в обычные интерфейсы привязки данных.
Проблема в том, что Rx, похоже, не поддерживает уведомление о том, что объект был удален из потока. Пример ниже.
Код показывает POCO, который использует класс BehaviorSubject для состояния поля. Код переходит к созданию коллекции этих объектов и использованию Concat для объединения потоков фильтров. Это означает, что о любых изменениях в POCO сообщается в один поток.
Фильтр для этого потока настроен на фильтрацию по рейтингу == 0. Подписка просто выводит результат в окно отладки, когда происходит событие четности.
Настройки рейтинга = 0 для любого элемента вызовут событие. Но если установить для параметра Rating значение 5, события не будут видны.
В случае CLINQ выходные данные запроса будут поддерживать INotifyCollectionChanged - поэтому элементы, добавленные и удаленные из результата запроса, будут вызывать правильное событие, указывающее, что результат запроса был изменен (элемент добавлен или удален).
Единственный способ, с помощью которого я могу думать об этом, - это настроить два потока с противоположными (двойными) запросами. Элемент, добавленный в противоположный поток, подразумевает удаление из набора результатов. В противном случае я мог бы просто использовать FromEvent и не делать ни одну из моделей сущностей наблюдаемой - что делает Rx больше всего лишь агрегатором событий. Есть указатели?
using System;
using System.ComponentModel;
using System.Linq;
using System.Collections.Generic;
namespace RxTest
{
public class TestEntity : Subject<TestEntity>, INotifyPropertyChanged
{
public IObservable<string> FileObservable { get; set; }
public IObservable<int> RatingObservable { get; set; }
public string File
{
get { return FileObservable.First(); }
set { (FileObservable as IObserver<string>).OnNext(value); }
}
public int Rating
{
get { return RatingObservable.First(); }
set { (RatingObservable as IObserver<int>).OnNext(value); }
}
public event PropertyChangedEventHandler PropertyChanged;
public TestEntity()
{
this.FileObservable = new BehaviorSubject<string>(string.Empty);
this.RatingObservable = new BehaviorSubject<int>(0);
this.FileObservable.Subscribe(f => { OnNotifyPropertyChanged("File"); });
this.RatingObservable.Subscribe(f => { OnNotifyPropertyChanged("Rating"); });
}
private void OnNotifyPropertyChanged(string property)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property));
// update the class Observable
OnNext(this);
}
}
public class TestModel
{
private List<TestEntity> collection { get; set; }
private IDisposable sub;
public TestModel()
{
this.collection = new List<TestEntity>() {
new TestEntity() { File = "MySong.mp3", Rating = 5 },
new TestEntity() { File = "Heart.mp3", Rating = 5 },
new TestEntity() { File = "KarmaPolice.mp3", Rating = 5 }};
var observableCollection = Observable.Concat<TestEntity>(this.collection.Cast<IObservable<TestEntity>>());
var filteredCollection = from entity in observableCollection
where entity.Rating==0
select entity;
this.sub = filteredCollection.Subscribe(entity =>
{
System.Diagnostics.Debug.WriteLine("Added :" + entity.File);
}
);
this.collection[0].Rating = 0;
this.collection[0].Rating = 5;
}
};
}