How use Observable.ToAsync with IEnumerable - c#

I have run into some problem with Rx. I need that after processing each item in a new thread send result to main thread. I did it with other way. How can i solve this task with Rx? Here is code:
Observable.ToAsync<string, IEnumerable<UserState>>(Run)(path)
.ObserveOnDispatcher<IEnumerable<UserState>>()
.Subscribe(
(o) =>
{ // need to run in Main Thread
foreach (var item in o)
{
WriteLog(item.StatusCode, item.Url);
}
},
(ex) =>{ },
() =>{ } );
// need to run in New Thread
private IEnumerable<UserState> Run(string sitemap)
{
....
foreach (var url in urls)
{
var result = new UserState
{
Url = url.Value,
TotalItems = urls.Count
};
....
yield return result;
}
}

You want to generate the IEnumerable on some other background thread and then Process each users object in this enumerable on Main UI thread, if this understanding is correct then you can do something like this:
var users = Run(path); //NOTE: This doesn't execute your run method yet, Run will only execute when you start enumerating the users values
users.ToObservable(System.Concurrency.Scheduler.ThreadPool) //The enumerator will be scheduled on separate thread
.ObserveOn(frm) //Observe on UI thread of win form
.Subscribe(s => {}) //This will run in UI thread for each user object

It would be good if you could describe the issue that you have.
What I can tell you at the moment is that you have a couple of potential issues.
First, using ObserveOnDispatcher is meant to work with the System.Windows.Threading.Dispatcher (usually created with WPF). If you're running your code outside of WPF it effectively means "the current thread" and this could lead to your subscription not being able to run if the current thread is busy. In other words, you might create a dead-lock.
I ran your code in both WPF and LINQPad and it worked fine in WPF, but dead-locked in LINQPad. If I observed on another thread then it worked fine in LINQPad and failed in WPF.
Secondly, you're turning an iterator method into an async observable and that won't work as you expect. An iterator doesn't actually run any code until you actually iterate through the enumerable. Essentially you return from Run almost instantly and you only execute the body of you Run method in the Subscribe code - and that's the wrong thread!
What you need to do is force immediate execution of the enumerable - at the very least - change your code to look like this:
private UserState[] Run(string sitemap)
{
...
Func</* url type */, UserState> create = url =>
{
var result = new UserState
{
Url = url.Value,
TotalItems = urls.Count
};
....
return result;
};
return (from url in urls select create(url)).ToArray();
}
Your main code needs to have a little clean up:
Observable.ToAsync<string, UserState[]>(Run)(path)
.ObserveOnDispatcher()
.Subscribe(o =>
{
foreach (var item in o)
{
WriteLog(item.StatusCode, item.Url);
}
});
Let me know if any of this helps.
EDIT: Added sample FromEventPattern code as per OP request in the comments.
Here's an example Windows Forms use of FromEventPattern. The first part creates a way to clean up subscriptions when the form closes.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Create a collection of IDisposable
// to allow clean-up of subscriptions
var subscriptions =
new System.Reactive.Disposables.CompositeDisposable();
var formClosings = Observable
.FromEventPattern<FormClosingEventHandler, FormClosingEventArgs>(
h => this.FormClosing += h,
h => this.FormClosing -= h);
// Add a subscription that cleans up subscriptions
// when the form closes
subscriptions.Add(
formClosings
.Subscribe(ea => subscriptions.Dispose()));
This next part watches for mouse drags on a picture box and creates messages to let the user know how far they've dragged.
var pictureBox1MouseDowns = Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => pictureBox1.MouseDown += h,
h => pictureBox1.MouseDown -= h);
var pictureBox1MouseMoves = Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => pictureBox1.MouseMove += h,
h => pictureBox1.MouseMove -= h);
var pictureBox1MouseUps = Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => pictureBox1.MouseUp += h,
h => pictureBox1.MouseUp -= h);
var pictureBox1MouseDrags =
from md in pictureBox1MouseDowns
from mm in pictureBox1MouseMoves.TakeUntil(pictureBox1MouseUps)
let dx = mm.EventArgs.Location.X - md.EventArgs.Location.X
let dy = mm.EventArgs.Location.Y - md.EventArgs.Location.Y
select new Point(dx, dy);
var pictureBox1MouseDragMessages =
from md in pictureBox1MouseDrags
let f = "You've dragged ({0}, {1}) from your starting point"
select String.Format(f, md.X, md.Y);
The next part tracks the number of times a button is clicked and creates a messages to display to the user.
var button1ClickCount = 0;
var button1Clicks = Observable
.FromEventPattern(
h => button1.Click += h,
h => button1.Click -= h);
var button1ClickCounts =
from c in button1Clicks
select ++button1ClickCount;
var button1ClickMessages =
from cc in button1ClickCounts
let f = "You clicked the button {0} time{1}"
select String.Format(f, cc, cc == 1 ? "" : "s");
Finally the two message obervables are merged together and are subscribed to, placing the message in a label.
var messages = pictureBox1MouseDragMessages
.Merge(button1ClickMessages);
// Add a subscription to display the
// merged messages in the label
subscriptions.Add(
messages
.Subscribe(m => label1.Text = m));
}
}
Keep in mind that all of this resides in the form's constructor and no module level fields or properties are used and all the events handlers are removed when the form closes. Very neat stuff.

Related

OpenSilver and Dispatcher.CheckAccess

I am working on porting an old Silverlight application over to OpenSilver. Throughout the Silverlight code there are if( <control>.CheckAccess())... to make sure to be on the correct thread. Is my impression this check is no longer needed in OpenSilver? In other words, the following Silverlight code can be transformed into the following:
Yes, I know that callback-based async methods have been replaced with awaitable tasks. I am going to ask some questions about that conversion in my next question, here. This question is exclusively about the fate of the Dispatcher.CheckAccess
Silverlight:
private void GetNextImage()
{
var cmc = ServiceFactories.CreateCartManager();
cmc.getSlideImageCompleted += (s, e) =>
{
if (imageGrid.CheckAccess())
{
cmc_getSlideImageCompleted(s, e);
}
else
{
var args = new object[] { s, e };
imageGrid.Dispatcher.BeginInvoke(new getSlideImageCompletedDelegate(cmc_getSlideImageCompleted),
args);
}
};
var lastTime = SystemSettings.GetInstance().SlideShowData.LastImageTime;
cmc.getSlideImageAsync(string.IsNullOrEmpty(lastTime) ? null : lastTime);
}
to OpenSilver:
private void GetNextImage()
{
var cmc = ServiceFactories.CreateCartManager();
cmc.getSlideImageCompleted += (s, e) =>
{
cmc_getSlideImageCompleted(s, e);
};
var lastTime = SystemSettings.GetInstance().SlideShowData.LastImageTime;
cmc.getSlideImageAsync(string.IsNullOrEmpty(lastTime) ? null : lastTime);
}
There is no need to use Dispatcher.CheckAccess since OpenSilver is currently single threaded (it uses mono.wasm runtime which doesn't support threads yet).
However, OpenSilver keeps compatibility with Silverlight, so if you have an old Silverlight code which does the check you can just keep it (it will always return true when running in Browser).

Akavache and collectionChanged event

(1) I am having trouble getting the CollectionChanged event of an ObservableCollection to fire whilst using Akavache, here is the code I have (simplified)
GraphCollection = new ObservableCollection<UserData>();
_cache.GetOrFetchObject(TabType.Graph.ToString(),
async () => await _dataStore.GetAllDocuments(TabType.Graph))
.Subscribe(
GraphList =>
{
GraphCollection = new ObservableCollection<UserData>(GraphList);
//GraphCollection.Shuffle();
NoGraphItems = GraphCollection.Count == 0;
});
GraphCollection.CollectionChanged += (sender, args) =>
{
NoGraphItems = GraphCollection.Count == 0;
};
Ideally, when I Add/Delete data I want to trigger the event to check to see if the collection is empty and then assign a bool property that it is or isn't empty.
I am making simple Add/Delete like so, and then calling a RefreshCache method to invalidate the data and recreate the data, not sure if this is the most efficient way to do it as well.
var graphRecord = GraphCollection.FirstOrDefault(x => x.Id == data.Id);
GraphCollection.Remove(dreamRecord);
RefreshCache(TabType.Graphs, DreamsCollection);
private void RefreshCache(TabType tabType, ObservableCollection<UserData> collection)
{
_cache.InvalidateObject<UserData>(tabType.ToString());
_cache.InsertObject(tabType.ToString(), collection);
}
(2) I am not currently setting the DateTime offset, do I need this? Can someone give me an example of how to write it out, the docs don't clearly state this.
DateTimeOffset? absoluteExpiration
your Subscribe creates a new instance of GraphCollection so the event handler that was assigned to the original instance no longer works with the new instance
try this instead
GraphList =>
{
GraphCollection = new ObservableCollection<UserData>(GraphList);
NoGraphItems = GraphCollection.Count == 0;
GraphCollection.CollectionChanged += // handler goes here
});

Observable.FromEventPattern(addHandler, removeHandler ) - simplification?

When creating an observable from an event, it looks like the following is the most common way:
var o = Observable.FromEventPattern(h => source.Event += h,
h => source.Event -= h);
I find this form a little tedious in some cases where I would like to handle multiple events in the same way. But that doesn't look to easy, since the event it self seems impossible to parameterize, as shown in this non-compiling code:
private IObservable MakeAnObservableFromThisEvent(Event event)
{
return Observable.FromEventPattern(h => event += h,
h => event -= h);
}
private void MakeAlotOfObservables(object source)
{
MakeAnObservableFromThisEvent(source.OneEvent);
MakeAnObservableFromThisEvent(source.AnotherEvent);
MakeAnObservableFromThisEvent(source.ThirdEvent);
//or even
MakeAnObservableFromThisEvent(() => source.ThirdEvent);
}
Of cause there is the 'event name'-overload:
var o = Observable.FromEventPattern< >(source, "Event");
but then there is this thing with more or less magic strings...
Is there away of optimizing this code? Or is this just the way things are?
The problem is that event handlers have "value-type" semantics (like strings) and so passing them as parameters is only useful if you intend to invoke them. Adding new handlers effectively creates a new delegate instance and the original is not modified.
Then only really viable method to add and remove handlers and maintain type safety at the same time is with the syntax you showed first in your question.
var o =
Observable
.FromEventPattern(
h => source.Event += h,
h => source.Event -= h);
However, there is another option that I've used quite a bit - and that's using extension methods.
If I have this class:
public class Foo
{
public event EventHandler<EventArgs> Click;
}
I can write an extension method:
public static class FooEx
{
public static IObservable<EventPattern<EventArgs>> Clicks(this Foo source)
{
return
Observable
.FromEventPattern<EventArgs>(
h => source.Click += h,
h => source.Click -= h);
}
}
That then allows me to write this:
var foo = new Foo();
foo.Clicks().Subscribe(x => Console.WriteLine("Click!"));
You effectively write the extension method once per type and event and then can use it where ever you need to with a much improved syntax.
It's not really a direct solution, but the ReactiveUI-Events basically implements what #Enigmativity suggests for the entire framework. So you can do something like:
Observable.Merge(
Foo.Events().Clicked.Select(_ => Unit.Default),
Foo.Events().KeyUp.Select(_ => Unit.Default));

Take from Observable.Interval until another observable produces a value

I am using RX to query the events that come out of an automation device, which has buttons connected to it. I want to be able to tell the difference when a user has just pressed and immediately released the button, or if he is holding the button down for some time. I'm having a problem with the query I use to see if he's holding the button down.
The basic idea is that once the button has been pressed, I would produce a value every half second, until the button was released again. I am also timestamping each value, so that I know how long the button has been pressed.
Here's my code, and how I think this should work:
public IObservable<DigitalInputHeldInfo> Adapt(IObservable<EventPattern<AdsNotificationEventArgs>> messageStream) {
var startObservable =
_digitalInputPressedEventAdapter.Adapt(messageStream);
var endObservable =
_digitalInputReleasedEventAdapter.Adapt(messageStream);
return from notification in startObservable.Timestamp()
from interval in
Observable.
Interval(
500.Milliseconds(),
_schedulerProvider.ThreadPool).
Timestamp().
TakeUntil(endObservable)
select new DigitalInputHeldInfo(
interval.Timestamp.Subtract(notification.Timestamp),
notification.Value);
}
From a given IObservable, I am applying a query on it so that I have an observable startObservable which produces a value everytime the button is pressed (state goes from false to true). I also have an observable endObservable, queried from the same source observable, which produces a value when the button is released again (state goes from true to false). When startObservable produces a value, I start an observable interval at every 500 milliseconds. I timestamp these values, and I take from it until endObservable produces a value. I then return an object holding the value of the startObservable and how long is has been held up until now.
I have a unit test for this where I hold the button for 2 seconds (using the TestScheduler), release it and then let the scheduler go on for a couple more seconds. I do this to make sure that no more values are produced after the button was released.
This is the point where my test fails, and the topic of my question. In my test, I am expecting 4 values to be produced (after 0.5s, 1s, 1.5s and 2s). However, there are still events being produced after the button was released, even though I am using TakeUntil(endObservable) (the digitalInputReleasedEventAdapter that produces the endObservable has its own set of tests, and I'm confident that it works the way it should).
I don't think I've constructed my query incorrectly. I suspect it might have something to do with hot vs. cold observables. But as I'm just starting out with RX, I don't quite have a full understanding of how that might relate to the issue I'm having here.
Not sure if this does exactly what you're after, but it's "yet another approach":
void Main()
{
// ButtonPush returns IObservable<bool>
var buttonPusher = ButtonPush();
var pushTracker =
from pushOn in buttonPusher.Where(p => p).Timestamp()
let tickWindow = Observable.Interval(TimeSpan.FromMilliseconds(500))
from tick in tickWindow
.TakeUntil(buttonPusher.Where(p => !p))
.Timestamp()
.Select(t => t.Timestamp)
select tick;
Console.WriteLine("Start: {0}", Observable.Return(true).Timestamp().First().Timestamp);
var s = pushTracker
.SubscribeOn(NewThreadScheduler.Default)
.Subscribe(x => Console.WriteLine("tick:{0}", x));
}
IObservable<bool> ButtonPush()
{
var push = new BehaviorSubject<bool>(false);
var rnd = new Random();
Task.Factory.StartNew(
() =>
{
// "press button" after random # of seconds
Thread.Sleep(rnd.Next(2, 5) * 1000);
push.OnNext(true);
// and stop after another random # of seconds
Thread.Sleep(rnd.Next(3, 10) * 1000);
push.OnNext(false);
});
return push;
}
It sounds like what you want is to only receive time events while the button is down. CombineLatest should help you here.
For example:
using Microsoft.Reactive.Testing;
using System;
using System.Diagnostics;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace ButtonTest
{
class Program
{
enum State
{
KeyDown, KeyUp
}
static void Main(string[] args)
{
var buttonState = new BehaviorSubject<State>(State.KeyUp);
var testScheduler = new TestScheduler();
var events = testScheduler.CreateObserver<long>();
Observable.Interval(TimeSpan.FromTicks(100), testScheduler)
.CombineLatest(buttonState, (t,s)=> new { TimeStamp = t, ButtonState = s })
.Where(t => t.ButtonState == State.KeyDown)
.Select(t => t.TimeStamp)
.Subscribe(events);
testScheduler.AdvanceBy(100);//t=0
testScheduler.AdvanceBy(100);//t=1
buttonState.OnNext(State.KeyDown);
testScheduler.AdvanceBy(100);//t=2
testScheduler.AdvanceBy(100);//t=3
buttonState.OnNext(State.KeyUp);
testScheduler.AdvanceBy(100);//t=4
testScheduler.AdvanceBy(100);//t=5
buttonState.OnNext(State.KeyDown);
testScheduler.AdvanceBy(100);//t=6
buttonState.OnNext(State.KeyUp);
testScheduler.AdvanceBy(100);//t=7
testScheduler.AdvanceBy(100);//t=8
Debug.Assert(events.Messages.Count == 5);
Debug.Assert(events.Messages[0].Value.Value == 1);
Debug.Assert(events.Messages[1].Value.Value == 2);
Debug.Assert(events.Messages[2].Value.Value == 3);
Debug.Assert(events.Messages[3].Value.Value == 5);
Debug.Assert(events.Messages[4].Value.Value == 6);
}
}
}
Not sure if this is the bug, but your TimeStamp call isn't incorporating the test scheduler. Check all operators that take an IScheduler parameter and make sure they're passing in the test scheduler.
I have found a solution.
public IObservable<DigitalInputHeldInfo> Adapt(
IObservable<EventPattern<AdsNotificationEventArgs>> messageStream) {
var startObservable = _digitalInputPressedEventAdapter.
Adapt(messageStream).
Publish();
var endObservable = _digitalInputReleasedEventAdapter.
Adapt(messageStream).
Publish();
startObservable.Connect();
endObservable.Connect();
return from notification in startObservable.Timestamp()
from interval in Observable.Interval(500.Milliseconds(),
_schedulerProvider.ThreadPool).
Timestamp().
TakeUntil(endObservable)
select new DigitalInputHeldInfo(
interval.Timestamp.Subtract(notification.Timestamp),
notification.Value);
}
I isolated this code into a console application, and constructed the source observable (here called messageStream) from an IEnumerable<> which yielded some true and false values. I saw that this IEnumerable<> was generated several times, so there must have been several threads started. I believe the produced values were being consumed by separate instances of Observable.Interval, and that these were racing against each other to receive the messages indicating the button release. So now I'm calling Publish() and Connect() on the startObservable and endObservable, and so the Observable.Interval instances share the same subscription.

What is the proper way to determine the end of a mouse drag using Rx?

I am slowly learning how to use Reactive Extensions for .NET with WPF. There a few beginner examples about how simple it is to write drag-drop or drawing routines but they are all extremely simple. I'm trying to go one step further and it's not obvious to me what the "proper" way is.
The examples all show how you can define streams of events from MouseDown, MouseMove, and MouseUp
var mouseDown = from evt in Observable.FromEvent<MouseButtonEventArgs>(..., "MouseDown")
select evt.EventArgs.GetPosition(...);
var mouseMoves = from evt in Observable.FromEvent<MouseEventArgs>(..., "MouseMove")
select evt.EventArgs.GetPosition(...);
var mouseUp = Observable.FromEvent<MouseButtonEventArgs>(..., "MouseUp");
And then how you can easily do things during a MouseDrag (this displays the co-ordinates of the rectangle created from the starting drag point to the current mouse position)
var mouseDrag = from start in mouseDown
from currentPosition in mouseMoves.TakeUntil(mouseUp)
select new Rect(Math.Min(start.X, currentPosition.X),
Math.Min(start.Y, currentPosition.Y),
Math.Abs(start.X - currentPosition.X),
Math.Abs(start.Y - currentPosition.Y));
mouseDrag.Subscribe(x =>
{
Info.Text = x.ToString();
});
My question is, what is the "proper" way to accomplish a task at the end of the mouse drag? Originally, I thought I could do something like this:
mouseDrag.Subscribe(
onNext: x =>
{
Info.Text = x.ToString();
},
onCompleted: () =>
{
// Do stuff here...except it never gets called
});
Reading more of the documentation, though, it seems that onCompleted is called when there is no more data (ever) and when the object can be disposed.
So the first option that seems plausable is subscribing to the mouseUp event and doing something there.
mouseUp.Subscribe(x =>
{
// Do stuff here..
}
But then at this point, I may as well go back to just use the "normal" MouseLeftButtonUp event handler.
Is there another way to determine when the mouseDrag is "completed" (or when the TakeUntil(mouseUp)) occurs and perform some action then?
The sequence never completes because the source (MouseDown) never completes (it is an event). It's worth pointing out that a IObservable cannot call OnComplete of a subscriber more than once, it's part of the contract (OnNext* (OnCompleted|OnError)?).
To find out when themouseMove.TakeUntil(mouseUp) sequence completes, you'll need to hook into the call to SelectMany:
public static IDisposable TrackDrag(this UIElement element,
Action<Rect> dragging, Action dragComplete)
{
var mouseDown = Observable.FromEvent(...);
var mouseMove = Observable.FromEvent(...);
var mouseUp = Observable.FromEvent(...);
return (from start in mouseDown
from currentPosition in mouseMove.TakeUntil(mouseUp)
.Do(_ => {}, () => dragComplete())
select new Rect(Math.Min(start.X, currentPosition.X),
Math.Min(start.Y, currentPosition.Y),
Math.Abs(start.X - currentPosition.X),
Math.Abs(start.Y - currentPosition.Y));
).Subscribe(dragging);
}
Then you can use it like so:
element.TrackDrag(
rect => { },
() => {}
);
For the interest of clarity, here is the LINQ expression using the underlying extension methods:
return mouseDown.SelectMany(start =>
{
return mouseMove
.TakeUntil(mouseUp)
.Do(_ => {}, () => dragComplete())
.Select(currentPosition => new Rect(...));
})
.Subscribe(dragging);
That is, for each value from mouseDown a new sequence will be subscribed to. When that sequence completes, call dragComplete().

Categories

Resources