I am trying to use Reactive Extensions to throttle PropertyChanged notifications. There are examples of doing this using GroupBy, but with one Subscription created for each PropertyName.
I want to handle the PropertyChanged event for all properties, and I need to Throttle those events for each PropertyName.
This is what I have so far, but it causes a deadlock.
ValuesPropertyChanged = Observable.FromEventPattern<PropertyChangedEventArgs>(value, "PropertyChanged")
.GroupBy(o => o.EventArgs.PropertyName)
.First()
.Throttle(TimeSpan.FromSeconds(2))
.Subscribe(args => HandlePropertyChanged(args.EventArgs.PropertyName));
The deadlock happens in the call to .First().
It still locks if I change that line to:
.Select(o => o.First())
I have also tried
.Select(o => o.FirstAsync())
The examples for GroupBy here look pretty concise, but I am incapable of wrapping my head around converting these examples to my solution.
Why does this cause a deadlock, and what should I do to make this work?
I think this might be what you're after:
// assume MyObj : INotifyPropertyChanged, naturally
var value = new MyObj();
Action<string> HandlePropertyChanged =
name => Console.WriteLine("Got a change for name:" + name);
// The query
var valuesPropertyChanged =
// create from event stream
from propChange in Observable.FromEventPattern<PropertyChangedEventArgs>(
value,
"PropertyChanged")
// group events by property name
group propChange by propChange.EventArgs.PropertyName into batchByName
// Throttle the resulting batch
from throttledByName in batchByName.Throttle(TimeSpan.FromSeconds(1))
// then select each item of the "throttled output"
select throttledByName;
valuesPropertyChanged.Subscribe(args =>
HandlePropertyChanged(args.EventArgs.PropertyName));
for(int i=0;i<10;i++)
{
value.Value1 = i.ToString();
value.Value2 = (i-1).ToString();
}
Output:
Got a change for name:Value2
Got a change for name:Value1
Here is the same but with extension methods:
var valuesPropertyChanged =
Observable.FromEventPattern<PropertyChangedEventArgs>(
_vm,
"PropertyChanged")
.GroupBy(propchange => propchange.EventArgs.PropertyName)
.Select(o => o.Throttle(TimeSpan.FromSeconds(1)))
.Merge();
Related
I'm trying to manipulate properties in a GroupBy clause to be used in a dictionary:
var lifeStages = await _dbContext.Customers
.GroupBy(x => GetLifeStage(x.DoB))
.Select(x => new { LifeStage = x.Key, Count = x.Count() })
.ToDictionaryAsync(x => x.LifeStage, x => x.Count);
I'm expecting results like
adolescent: 10,
adult: 15,
senior: 12 etc
But getting error:
Either rewrite the query in a form that can be translated,
or switch to client evaluation explicitly
by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
Offcourse I can't combine ToDictionary() with any of the mentioned calls, and splitting up the query did not resolve the issues or taught my anything)
I've tried with making GetLifeStage() static and async, no difference there as well. The method gets called, performs what it needs to do, and still GroupBy can't be translated
If I leave out the Select() part and work with the Key of the GroupBy, same error:
"...could not be translated."
I saw an error too that said I couldn't combine a GroupBy() with a ToDictionary() during try-outs, but doesn't seem to pop up atm.
As I'm running out of ideas, all suggestions are welcome!
update:
private LifeStage GetLifeStage(DateTimeOffset doB)
{
var ageInMonths = Math.Abs(12 * (doB.Year - DateTimeOffset.UtcNow.Year) + doB.Month - DateTimeOffset.UtcNow.Month);
switch (ageInMonths)
{
case < 216:
return LifeStage.Adolescent;
case < 780:
return LifeStage.Adult;
case >= 780:
return LifeStage.Senior;
}
}
The problem is the usage of the custom GetLifeStage method inside the GroupBy expression. Custom methods cannot be translated to SQL because the query translator code has no way to know what is inside that method. And it cannot be called because there are no objects at all during the translation process.
In order to make it translatable, you have to replace the custom method call with its body, converted to translatable expression - basically something which can be used as expression bodied method. You can't use variables and switch, but you can use conditional operators. Instead of variable, you could use intermediate projection (Select).
Here is the equivalent translatable query:
var lifeStages = await _dbContext.Customers
.Select(c => new { Customer = c, AgeInMonths = Math.Abs(12 * (c.DoB.Year - DateTimeOffset.UtcNow.Year) + c.DoB.Month - DateTimeOffset.UtcNow.Month) })
.GroupBy(x => x.AgeInMonths < 216 ? LifeStage.Adolescent : x.AgeInMonths < 780 ? LifeStage.Adult : LifeStage.Senior)
// the rest is the same as original
.Select(x => new { LifeStage = x.Key, Count = x.Count() })
.ToDictionaryAsync(x => x.LifeStage, x => x.Count);
With https://github.com/neuecc/UniRx,
I have two observables A and B.
I want A to be filtered by B. Sample seems like what I want but the negative of it.
IObservable<long> A = Observable.EveryUpdate();
IObservable<Collider2D> B = this.OnTriggerEnter2DAsObservable()
.Where( x => x.gameObject.tag == "Wall");
I want some kind of Pseudo code like that:
A.filterBy(B)
.Subscribe(x => Debug.Log(x)); //executed only when B is not streaming
(Update1)
Here is actual code. I am trying to cancel out input stream with colliding stream.
var isCollidingWithWall = this.OnTriggerEnter2DAsObservable()
.Where(collider => collider.gameObject.tag == "Wall");
Func<long, float> displaceCalculate = (_) => this.Speed * Time.deltaTime;
var moveLeft = Observable.EveryUpdate()
.Where(_ => Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow));
var moveRight = Observable.EveryUpdate()
.Where(_ => Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow));
var movement1 = moveLeft
.Select(displaceCalculate)
.Select(f => -f);
var movement2 = moveRight
.Select(displaceCalculate);
movement2
.Merge(movement1)
.Subscribe(f =>
{
this.transform.position = new Vector2(this.transform.position.x + f, this.transform.position.y);
});
I think I might be going in wrong direction.
It is difficult to only combine operators.
The two streams are not synchronized.
When the OnNext message comes from stream B, how long shut off stream A?
Next stream B message? or Next stream A?
If you want to stop it only one frame, how about this?
void Start()
{
var isCollisionEntered = false;
this.OnCollisionEnter2DAsObservable()
.Where(x => x.gameObject.tag == "Wall")
.Subscribe(_ => isCollisionEntered = true);
this.LateUpdateAsObservable()
.Where(_ => isCollisionEntered)
.Subscribe(_ => isCollisionEntered = false);
this.UpdateAsObservable()
.Where(_ => !isCollisionEntered)
.Subscribe(_ => Debug.Log("Do here"));
}
And, I don't recommend Observable.EveryUpdate .It is necessary to manage lifetime.
I recommend using this.UpdateAsObservable (UniRx.Triggers) instead.
It automatically publishes OnCompleted message on the gameobject destroyed.
I just came up with another way.
var streamB = this.OnTriggerEnter2DAsObservable().AsUnitObservable();
this.UpdateAsObservable()
.TakeUntil(streamB)
.RepeatUntilDestroy(this)
.Subscribe(_ =>
{
Debug.Log(Time.frameCount);
});
Can you provide a little more context about the actual game behavior you are trying to implement?
My guess would be that there is some other approach to what you are trying to do, without having to rely on EveryUpdate (e.g. by using OnTriggerStay and/or OnTriggerExit).
Just giving a guess to what you mean by "negative" of the sample operator: you might want to have a look at pausable. You'd have to generate the proper boolean values though, and how to do that really depends on what game behavior you are actually trying to implement here.
As I understand, ordered assertions in FakeItEasy 2 are done like this (from the docs):
// Assert
A.CallTo(() => unitOfWorkFactory.BeginWork()).MustHaveHappened()
.Then(A.CallTo(() => usefulCollaborator.JustDoIt()).MustHaveHappened())
.Then(A.CallTo(() => unitOfWork.Dispose()).MustHaveHappened());
Now, suppose I have a collection and for each item in this collection I want to assert that a call was made to a faked object. What is the best approach to assert the calls were made in the correct order?
I came up with this, but don't really like it:
IOrderableCallAssertion ioca = null;
foreach (var item in items.OrderBy(i => i.Id)
{
var itemUnderTest = item;
if (ioca == null)
{
ioca = A.CallTo(() => fakeObject.Handle(itemUnderTest, otherArgument)).MustHaveHappened(Repeated.Exactly.Once);
}
else
{
ioca = ioca.Then(A.CallTo(() => fakeObject.Handle(itemUnderTest, otherArgument)).MustHaveHappened(Repeated.Exactly.Once));
}
}
That looks about right to me. Of course, you could inline itemUnderTest and pull MustHaveHappened outside of the two if branches.
And you could always hide this in a convenience method.
An alternative: use Invokes to capture the fakes as the calls come in and later compare them against a list.
I have a media application that allows the user to Play, Pause, step frame by frame, FastForward, etc. I am attempting to use Rx to get the following behavior for stepping and FastForward.
If the user clicks the right arrow less than 2 times/300ms I want to frame step.
If the user holds down the right arrow I want to fast forward until the right arrow button is released.
I think I have the fast forward part correct, but am not sure how to craft this to get the step functionality. I am also open to "better" ways to do the fast forward.
//start FF when we get 2 key presses within the threshold time
Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown")
.Where(k => k.EventArgs.Key == Key.Right)
.Timestamp()
.Buffer(2)
.Where(x => (x[1].Timestamp - x[0].Timestamp).Milliseconds < 300)
.Subscribe(x =>
{
Console.WriteLine("FastForward GO");
_viewModel.FastForward();
});
//stop ff on the key up
Observable.FromEventPattern<KeyEventArgs>(this, "KeyUp")
.Where(k => k.EventArgs.Key == Key.Right)
.Subscribe(x => {
Console.WriteLine("FastForward STOP");
_viewModel.StopFastForward();
});
Solution
var up = Observable.FromEventPattern<KeyEventArgs>(this, "KeyUp")
.Where(x => x.EventArgs.KeyCode == Keys.Right);
// Take, Concat, and Repeat work together to prevent repeated KeyDown events.
var down = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown")
.Where(x => x.EventArgs.KeyCode == Keys.Right)
.Take(1)
.Concat(up.Take(1).IgnoreElements())
.Repeat();
var t = TimeSpan.FromMilliseconds(300);
var tap = down.SelectMany(x =>
Observable.Amb(
Observable.Empty<EventPattern<KeyEventArgs>>().Delay(t),
up.Take(1)
))
.Publish()
.RefCount();
var longPress = down.SelectMany(x =>
Observable.Return(x).Delay(t).TakeUntil(tap)
);
There's multiple ways to do this, but this works at getting the "longPress" you need, as well as the "tap". You can use longPress to start fast-fowarding, up to stop fast-forwarding, and tap for frame-stepping.
tap yields when a key has been pressed and released within a timespan of t.
longPress yields when the key has been held down for longer than t.
up yields when the key has been released.
Explaination
A problem exists because the KeyDown event is repeated multiple times for each physical press of a key.
var down = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown");
In this case, we need a way to filter out the repeated KeyDown events. We can do that by using a combination of operators. First, we'll use Take(1). This will yield the first event and ignore the rest.
var first = down.Take(1);
If we only needed to get a single actual key press, this would be great. But, alas, we need to get all of the actual key presses. We need to wait for the KeyUp event to occur and start the whole thing over. To do this, we can use a combination of Concat and Repeat. For the concat observable, we need to make sure we're only taking 1 up event, and that we're ignore the elements of the up observable, otherwise we end up feeding all of the up events into our new observable.
var down = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown")
.Take(1)
.Contact(up.Take(1).IgnoreElements())
.Repeat();
This gives us the actual down events, without the in-between repeated events.
Now that we've cleaned up our source observables, we can start composing them in useful ways. What we're looking for is a "tap" event and a "long press" event. To get the tap event, we need to take a single actual down event, and make sure that it isn't held down too long... One way to do this is using the Amb operator.
var tap = down.SelectMany(x =>
Observable.Amb(
Observable.Empty<EventPattern<KeyEventArgs>>().Delay(t),
up.Take(1)
))
The Amb operator stands for "ambiguous". It takes a number of Observables, listens to each one, and waits for them to yield something. Once one of them yields an event, the Amb operator ignores (disposes the subscriptions of) the other observables.
In our case, for each down event that occurs, we use the SelectMany and Amb operator to check to see which yields or completes first... a single up event, or an empty observable that completes after a timespan of t. If the up event occurs before the the empty observable completes, its a tap. Otherwise, we ignore it.
Now we can do a similar thing for "long press", except this time we want to delay the KeyDown event until we know that it's not a tap. We can use a combination of the Delay and TakeUntil operators to do this. Delay makes sure the long press doesn't occur before a tap can be registered, and TakeUntil makes sure we ignore the KeyPress if it turned out to be a tap after all.
var longPress = down.SelectMany(x =>
Observable.Return(x).Delay(t).TakeUntil(tap)
);
Generalized Solution
This version works for any key.
var up = Observable.FromEventPattern<KeyEventArgs>(this, "KeyUp");
var downWithRepeats = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown");
var down =
Observable.Merge(
up.Select(x => new { e = x, type = "KeyUp" }),
downWithRepeats.Select(x => new { e = x, type = "KeyDown" })
)
.GroupByUntil(
x => x.e.EventArgs.KeyCode,
g => g.Where(y => y.type == "KeyUp")
)
.SelectMany(x => x.FirstAsync())
.Select(x => x.e);
var t = TimeSpan.FromMilliseconds(300);
var tap = down.SelectMany(x =>
Observable.Amb(
Observable.Empty<EventPattern<KeyEventArgs>>().Delay(t),
up.Where(y => y.EventArgs.KeyCode == x.EventArgs.KeyCode).Take(1)
))
.Publish()
.RefCount();
var longPress = down.SelectMany(x =>
Observable.Return(x).Delay(t).TakeUntil(
tap.Where(y => y.EventArgs.KeyCode == x.EventArgs.KeyCode)
)
);
Usage
Observable.Merge(
down .Select(x => string.Format("{0} - press", x.EventArgs.KeyCode)),
tap .Select(x => string.Format("{0} - tap", x.EventArgs.KeyCode)),
longPress.Select(x => string.Format("{0} - longPress", x.EventArgs.KeyCode)),
up .Select(x => string.Format("{0} - up", x.EventArgs.KeyCode))
)
.ObserveOn(SynchronizationContext.Current)
.Select(x => string.Format("{0} - {1}", x, DateTime.Now.ToLongTimeString()))
.Subscribe(text => this.myTextBox.Text = text);
Here's an alternative to Chris's that gives three streams, one for clicks, one for begin holds and one for end holds. Makes use of TimeInterval for recording duration between events.
WinForms Version
We can capture KeyDown eliminating repeats by using GroupByUntil to group KeyDown until a KeyUp occurs:
TimeSpan limit = TimeSpan.FromMilliseconds(300);
var key = Keys.Right;
var keyUp = Observable.FromEventPattern<KeyEventArgs>(this, "KeyUp")
.Where(i => i.EventArgs.KeyCode == key)
.Select(_ => true);
var keyDown = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown")
.Where(i => i.EventArgs.KeyCode == key)
.GroupByUntil(k => 0, _ => keyUp)
.SelectMany(x => x.FirstAsync());
var keyDownDuration = keyDown.Select(k => keyUp.TimeInterval()).Switch();
var clicks = keyDownDuration.Where(i => i.Interval < limit);
var beginHold = keyDown.Select(k => Observable.Timer(limit).TakeUntil(keyUp))
.Switch();
var endHold = keyDownDuration.Where(i => i.Interval > limit);
/* usage */
clicks.Subscribe(_ => Console.WriteLine("Click"));
beginHold.Subscribe(_ => Console.WriteLine("Hold Begin"));
endHold.Subscribe(_ => Console.WriteLine("Hold End"));
WPF Version
Originally, I had mistakenly assumed the WPF flavour of KevEventArgs as IsRepeat is not available in the WinForms version - which means this won't work for OP, but I'll leave it in as it may be of use for others.
TimeSpan limit = TimeSpan.FromMilliseconds(300);
var key = Key.Right;
var keyUp = Observable.FromEventPattern<KeyEventArgs>(this, "KeyUp")
.Where(i => i.EventArgs.Key == key);
var keyDown = Observable.FromEventPattern<KeyEventArgs>(this, "KeyDown")
.Where(i => i.EventArgs.IsRepeat == false
&& i.EventArgs.Key == key);
var keyDownDuration = keyDown.Select(k => keyUp.TimeInterval()).Switch();
var clicks = keyDownDuration.Where(i => i.Interval < limit);
var beginHold = keyDown.Select(k => Observable.Timer(limit).TakeUntil(keyUp))
.Switch();
var endHold = keyDownDuration.Where(i => i.Interval > limit);
/* usage */
clicks.Subscribe(_ => Console.WriteLine("Click"));
beginHold.Subscribe(_ => Console.WriteLine("Hold Begin"));
endHold.Subscribe(_ => Console.WriteLine("Hold End"));
To Test The Code
Include nuget package rx-main and paste the WinForms/WPF or code snippets as appropriate to the end of the Form contructor. Then run the code and press the right arrow key whilst observing the VS Output window to see the result.
Here we have a Observable Sequence... in .NET using Rx.
var aSource = new Subject<int>();
var bSource = new Subject<int>();
var paired = Observable
.Merge(aSource, bSource)
.GroupBy(i => i).SelectMany(g => g.Buffer(2).Take(1));
paired.Subscribe(g => Console.WriteLine("{0}:{1}", g.ElementAt(0), g.ElementAt(1)));
aSource.OnNext(4);
bSource.OnNext(1);
aSource.OnNext(2);
bSource.OnNext(5);
aSource.OnNext(3);
bSource.OnNext(3);
aSource.OnNext(5);
bSource.OnNext(2);
aSource.OnNext(1);
bSource.OnNext(4);
Output:
3:3
5:5
2:2
1:1
4:4
We will get events every time a pair of numbers arrive with the same id.
Perfect! Just what i want.
Groups of two, paired by value.
Next question....
How to get a selectmany/buffer for sequences of values.
So 1,2,3,4,5 arrives at both aSource and bSource via OnNext(). Then fire ConsoleWriteLine() for 1-5. Then when 2,3,4,5,6 arrives, we get another console.writeline(). Any clues anyone?
Immediately, the Rx forum suggests looking at .Window()
http://introtorx.com/Content/v1.0.10621.0/17_SequencesOfCoincidence.html
Which on the surface looks perfect. In my case i need a window of value 4, in this case.
Where in the query sequence does it belong to get this effect?
var paired = Observable.Merge(aSource, bSource).GroupBy(i => i).SelectMany(g => g.Buffer(2).Take(1));
Output
1,2,3,4,5 : 1,2,3,4,5
2,3,4,5,6 : 2,3,4,5,6
Regards,
Daniel
Assuming events arrive randomly at the sources, use my answer to "Reordering events with Reactive Extensions" to get the events in order.
Then use Observable.Buffer to create a sliding buffer:
// get this using the OrderedCollect/Sort in the referenced question
IObservable<int> orderedSource;
// then subscribe to this
orderedSource.Buffer(5, 1);
Here is an extension method that fires when it has n inputs of the same ids.
public static class RxExtension
{
public static IObservable<TSource> MergeBuffer<TSource>(this IObservable<TSource> source, Func<TSource, int> keySelector, Func<IList<TSource>,TSource> mergeFunction, int bufferCount)
{
return Observable.Create<TSource>(o => {
var buffer = new Dictionary<int, IList<TSource>>();
return source.Subscribe<TSource>(i =>
{
var index = keySelector(i);
if (buffer.ContainsKey(index))
{
buffer[index].Add(i);
}
else
{
buffer.Add(index, new List<TSource>(){i});
}
if (buffer.Count==bufferCount)
{
o.OnNext(mergeFunction(buffer[index]));
buffer.Remove(index);
}
});
});
}
}
Calling the extension.
mainInput = Observable.Merge(inputNodes.ToArray()).MergeBuffer<NodeData>(x => x.id, x => MergeData(x), 1);