Я изучал возможность использования Rx в среде MVVM. Идея состоит в том, чтобы использовать «живые» запросы LINQ к наборам данных в памяти для проецирования данных в модели представления для привязки.
Ранее это было возможно с использованием INotifyPropertyChanged / INotifyCollectionChanged и библиотеки с открытым исходным кодом под названием CLINQ . Потенциал с Rx и IObservable заключается в переходе к гораздо более декларативной модели ViewModel с использованием классов Subject для передачи измененных событий из исходной модели во View. На последнем этапе потребуется преобразование из IObservable в обычные интерфейсы привязки данных.
Проблема в том, что Rx, похоже, не поддерживает уведомление о том, что объект был удален из потока. Пример ниже.
Код показывает POCO, который использует класс BehaviorSubject для состояния поля. Код переходит к созданию коллекции этих сущностей и использованию Concat для объединения потоков фильтров. Это означает, что о любых изменениях POCO сообщается в один поток.
Фильтр для этого потока настроен на фильтрацию для рейтинга == 0. Подписка просто выводит результат в окно отладки при возникновении четности.
Рейтинг настроек = 0 для любого элемента вызовет событие. Но установка рейтинга обратно на 5 не приведет к появлению каких-либо событий.
В случае CLINQ вывод запроса будет поддерживать INotifyCollectionChanged - так что элементы, добавленные и удаленные из результата запроса, будут вызывать правильное событие, чтобы указать, что результат запроса имеет изменено (элемент добавлен или удален).
Единственный способ, которым я могу решить эту проблему, - это настроить два потока с противоположными (двойными) запросами. Элемент, добавленный в противоположный поток, подразумевает удаление из набора результатов. В противном случае я мог бы просто использовать FromEvent и не делать какие-либо модели сущностей наблюдаемыми, что делает Rx больше просто агрегатором событий. Есть указатели?
using System;
using System.ComponentModel;
using System.Linq;
using System.Collections.Generic;
namespace RxTest
{
public class TestEntity : Subject, INotifyPropertyChanged
{
public IObservable FileObservable { get; set; }
public IObservable RatingObservable { get; set; }
public string File
{
get { return FileObservable.First(); }
set { (FileObservable as IObserver).OnNext(value); }
}
public int Rating
{
get { return RatingObservable.First(); }
set { (RatingObservable as IObserver).OnNext(value); }
}
public event PropertyChangedEventHandler PropertyChanged;
public TestEntity()
{
this.FileObservable = new BehaviorSubject(string.Empty);
this.RatingObservable = new BehaviorSubject(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 collection { get; set; }
private IDisposable sub;
public TestModel()
{
this.collection = new List() {
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(this.collection.Cast>());
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;
}
};
}