ReactiveCommand depending two command on each other - c#

As a reaction from Shane Neuville I'm changing my question. But yes. It may be more complicated then I first thought it was.
I have a ReactiveList<ServiceEntryModel> Services; that will be populated when the view have appeared. With every entry in the list I have used the function called Services.ItemsAdded.Subscribe() where the RxCell will be populated with data that at the end of the whole list should be ordered in a specific way. As of now I have have two separate ReactiveCommands.
1.ReactiveCommand<Unit, List<ServiceEntryModel>> RefreshServices;
2.ReactiveCommand<Unit, List<ServiceEntryModel>> OrderByCommand;
The second command should be called when the whole list is populated from the Services.ItemsAdded.Subscribe method. How can I check/know when every entry is done?
I have tried something with counters and ObservablePropertyHelpers, but without any luck so far.
Here is my ViewModel part that should be relevant
Services.ItemsAdded.Subscribe(async entryItem =>
{
ServiceEntityStatusDto serviceEntityStatus = await GetServiceEntryStatus(entryItem);
if (serviceEntityStatus != null)
{
entryItem.AvailabilityStatus = serviceEntityStatus.AvailabilityStatus;
entryItem.PerformanceStatus = serviceEntityStatus.PerformanceStatus;
}
counterServices++;
//await OrderServices.Execute(); //TODO infinite loop ?
});
The part is commented is a infinite loop, because the Services.ItemsAdded will be continue to be called.
The user can pull to refresh and the first command will be called again, so right when that is done. The seconds should order the whole list again.

Is this enough?
RefreshServices
.SelectMany(_ => OrderServices.Execute())
.Subscribe();
Or the InvokeCommand syntax
RefreshServices
.InvokeCommand(OrderServices);
The distinction being that with InvokeCommand OrderServices will not execute if it's already executing which might not fit your use case. For example maybe RefreshServices can execute again while OrderServices is still running.

Related

Reactive Extensions unfold / scan approach for nested hierarchy

I'm currently building a wizard system for an application, and we're using ReactiveUI and as a result Rx.
Each step in the wizard implements IWizardStep<T> where T is just the data type that the wizard ultimately produces.
Each step has the capability of surfacing which step should be available for the user to move to next, so as to enable branching based on data entry.
The step can be thought of having a similar structure to:
public interface IWizardStep<T>
{
IObservable<IStepChoice<T>> NextStepChoice {get;}
}
With IStepChoice<T> simply being:
public interface IStepChoice<T>
{
IWizardStep<T> Step {get;}
string Reason {get;}
}
In order to calculate the current path from the start to the end, for display to the user, I need to be able to go from the starting step, and walk along the NextStepChoice chain recursively until it hits a null (it's valid behaviour for the NextStepChoice observable to emit a null to indicate the end of the wizard).
I've taken a look at Observable.Scan but can't for the life of me work out how to get this working properly recursively.
I've also taken a look at Observable.Generate which looks promising, as this is a classic unfold-esque problem; the only issue is that Generate takes a function to determine when to break the loop, but I need to evaluate the inner observable to work this out.
Observable.Generate(
new WizardStepChoice<T>(start, null),
choice => choice != null,
choice => choice.ChosenStep.NextStepChoice,
choice => choice);
This would be ideal, and produce the output I'm after, but the NextStepChoice selector there obviously doesn't compile because it's an IObservable<IWizardStepChoice<T>> rather than an IWizardStepChoice<T>.
I've looked at using Aggregate and Scan but as these are more fold-driven operations, and I've only got the starting element, it's an unfold I'm looking for ala Generate, but I need it to be able to evaluate the nested observable.
Would Observable.Create perhaps be something I could utilise? I've tried it and come up with:
Path = CurrentStep.Select(_ => Observable.Create<IWizardStep<T>>(async observer =>
{
IWizardStepChoice<T> next = new WizardStepChoice<T>(start, null);
observer.OnNext(next.ChosenStep);
while (next != null)
{
next = await next.ChosenStep.NextStepChoice;
observer.OnNext(next.ChosenStep);
}
observer.OnCompleted();
return Disposable.Empty;
}).Aggregate(new List<IWizardStep<T>>(),
(l, s) =>
{
l.Add(s);
return l;
})).Switch().Publish().RefCount();
Which has all the right signature I want IWizardStep<T>->IReadOnlyList<IWizardStep<T>>, so at first glance it looks right, but it doesn't work; it fires, and I can step through, but it hangs once it hits the await and doesn't come back.
I've got a feeling I'm close, and this is a scheduling issue, so my question really is this:
What is the best approach to solve this, am I close?
If this is right, why is there an issue with the await, and how might I solve it?
Update
After a little bit of tinkering I noticed that the await was likely hanging as that observable hadn't yet (and wasn't going to) emit a value (duh), which I've now resolved by initialising each step with a value at the beginning of the wizard.
I've even sanity-checked this by adding a property to IWizardStep<T> - IWizardStepChoice<T> LatestStepChoice {get;} which is just hooked up with:
NextStepChoice.Subscribe(c => _latestStepChoice = c);
This is done on the step class itself, and I can confirm it's working just fine.
Yet still the await hangs, so I tried:
Making it Replay(1) so the await calling .Subscribe() would get the value - this didn't work
Making it Repeat() so even if something is subscribed it'll see the new value - this just made the whole thing hang.
Clearly I'm missing something here, I want it so that when the await queries the observable, it will be given the latest value seen, which is what I thought Replay(1) would achieve; I've also tried PublishLast() but then future updates don't get honoured because of the AsyncSubject<T> behaviour.
For now I've switched to using the self-subscribed property, but it's not ideal, I'd rather not have to break out of querying the observables if I can help it, it feels "hacky".
A recursive walk can transform the tree of observables into a single observable:
static IObservable<IWizardStep<T>> Walk<T>(IWizardStep<T> step)
{
if (step?.NextStepChoice == null)
return Observable.Return(step);
return step.NextStepChoice.SelectMany(choice => Walk(choice.Step)).StartWith(step);
}
Usage:
var steps = await Walk(step).ToArray();

Wants next Stack<T> from the list of Actions

I am making a UndoRedo Functionlity for my application. Now on function DoAction I have the list of Action taken in Stack<UndoRedoAction> Now I want to get the Last Action Taken Which automatically will be First in the List To take out the First in the List I have used actionList.Peek(); Now the Situation arise is that the next time I wanted to take the second one from the list. I am not sure how to do that
private void DoAction(Stack<UndoRedoAction> actionList, Stack<UndoRedoAction> storeList, Action<UndoRedoAction> action)
{
if (actionList.Count > 0)
{
UndoRedoAction urAction = actionList.Peek();
action(urAction);
storeList.Push(urAction);
}
}
You need to use Stack<T>.Pop instead of Peek. Pop removes the last added item in the stack and returns it while Peek returns the last added item without removing it from the stack.
Use Pop for sure (as #Matias) said.
Also check out the Command Pattern as it is perfect for getting abstraction between the code that actually does the work and provides a great basis for undo.

Why I can't use List.Add() on Parallel.For or Parallel.Foreach?

This is my code :
Parallel.ForEach(Students, item =>
{
StudentModel studentModel = new StudentModel(item);
// Maybe he/she has alot of name
foreach (var words in studentModel.StudentNames.Split(','))
{
if (string.IsNullOrWhiteSpace(words))
return;
string tempWords = words.Trim();
// add it to student names list
STUDENT_NAMES.Where(x => x.SearchWords == tempWords).FirstOrDefault().student.Add(studentModel);
}
// add it to student list
STUDENT_MODELS.Add(studentModel);
});
What I want to do is, I get the student list. convert it to student model, get student names(because one student has a lot names), then I add the names to names list, that's because maybe later I need get same name students and do some thing.....
finally add student to student models list.
The problem happening on :
STUDENT_NAMES.Where(x => x.SearchWords == tempWords).FirstOrDefault().student.Add(studentModel);
This place always happening : System.IndexOutOfRangeException
I already change the Paralle.Foreach to Parallel.For , and change the foreach to for, but there is nothing changed.
I must use Parallel because the students count about 100000, if I just use foreach replace Parallel.Foreach, it will take 160+ second, If I lock that place.....still slow......if use Parallel.Foreach, it will use 20 second around, but I can't handle the exception.
I already try use this replace it:
StudentNames name = STUDENT_NAMES.Where(x => x.SearchWords == tempWords).FirstOrDefault();
if (null != name)
name.student.Add(StudentModel);
But the problem still happening on some times........
if I just try...catch it and ignore it, then When I later access the STUDENT_NAMES list , it still throw that exception............
I also try use ConcurrentBag<> , but the speed very slow....I can't take it.....
Is there any good way to handle it please. Thank you very much!
UPDATE:
What I can't understand is: Why I can't add some thing to the list in the Parallel.Foreach. I think Parallel.Foreach will use a lot thread , but event use multiple thread add is not any problem with it.
thread 1 add and thread 2 add not any relationship.... Why there is happening that exception?
List is not designed to be accessed from multiple threads. it is not safe to do so. You can run into all sorts of problems, ranging from out of index exceptions, additions being lost, duplicated, other types of errors, really just about anything.
I also try use ConcurrentBag<> , but the speed very slow....I can't take it.....
Performance is irrelevant when only one of the two options actually works. You can have code that works correctly and takes as long as that takes, or you can have code that doesn't work, and to which how fast it does the wrong this is irrelevant.
Of course you have other options, such as looking at ways of redesigning your program to use an inherently more effective algorithm instead of trying to do something really inefficient and just parallelism it to hide that fact. The fact that you're currently performing a liner search on your list to find a matching item for each item in another collection is a rather poor design; you should use a collection that can be more efficiently searched, such as a HashSet. Without knowing the details of the problem though, we couldn't possibly say what the proper design of the application should be.

Count + List with Futures not working

I'm having problems with Futures in Nhibernate 3 and can't realize what's wrong.
The following code (without Futures), works as expected:
SessionHandler.DoInTransaction(transaction =>
{
var criteria = SessionHandler.Session.CreateCriteria<T>();
var clonedCriteria = (ICriteria)criteria.Clone();
var count = criteria
.SetProjection(Projections.RowCount())
.UniqueResult<Int32>();
var result = clonedCriteria
.SetMaxResults(PageSize)
.SetFirstResult(page * PageSize)
.List<T>();
ItemList = result;
TotalResults = count;
RecalculatePageCount();
});
SessionHandler just stores a Session for this context, and DoInTransaction is a convenience method:
public void DoInTransaction(Action<ITransaction> action)
{
using (var transaction = Session.BeginTransaction())
{
action(transaction);
transaction.Commit();
}
}
Now, the following code causes GenericAdoException:
SessionHandler.DoInTransaction(transaction =>
{
var criteria = SessionHandler.Session.CreateCriteria<T>();
var clonedCriteria = (ICriteria)criteria.Clone();
var count = criteria
.SetProjection(Projections.RowCount())
.FutureValue<Int32>();
var result = clonedCriteria
.SetMaxResults(PageSize)
.SetFirstResult(page * PageSize)
.Future<T>();
ItemList = result;
TotalResults = count.Value;
RecalculatePageCount();
});
I'm using PostgreSQL 9.2, Npgsql 2.0.11.0 and NHibernate 3.3.1.4000. If that matters, I use Fluent NHibernate for my mappings
Thank you for any advice.
EDIT:
After more research, I found that this error only occurrs after I add an item. At starting, I'm loading data in my form, and it works just fine. I get the exception when I reload the data in my form after adding an item. But it is pretty strange. The item is added correctly. The code for adding or updating items looks like this:
if (IsEditing)
{
SessionHandler.DoInTransaction(tx => SessionHandler.Session.Update(CurrentItem));
}
else
{
SessionHandler.DoInTransaction(tx => SessionHandler.Session.Save(CurrentItem));
}
What is strange is that I (sometimes, I think) get this exception when I'm raising the PropertyChanged event. I noticed that sometimes the InnerException is different. Sounds like a threading problem, but it is strange that it works without futures. But I'm not using threads for loading the data, just for adding items (hmm, but maybe, because I notify when my items are added, and I load the items in answer to "that message", I think that load would be executed in another thread)
EDIT 2:
The error seems pretty random. Sometimes I get it, sometimes not :S
I think I've found the issue.
This may sound stupid, but I think it makes sense. I had to swap these lines:
ItemList = result;
TotalResults = count.Value;
So they resulted
TotalResults = count.Value;
ItemList = result;
The problem was, basically, multithreading (I think I didn't mentioned it pretty much in my question, but the randomness of the errors were a bit suspicious). So first, I'll tell you some background so the solution is clearer:
When a new element is added to the database, a message is (globally) sent, so everyone 'interested' can update its elements to reflect the changes. As I'm using MVVM Light, I do it like this:
Messenger.Default.Send(new DataReloadSuggested<MyEntityType>(theUpdatedId));
I was using Tasks to add the elements, so when I clicked on the 'Add' button, something like this was executed:
Task.Factory.StartNew(CommitCurrentItem);
And, inside CommitCurrentItem, I added the item to the database and notified the program that the list was updated (sending the message as mentioned above).
My main form registered to that messages, like this:
Messenger.Default.Register<DataReloadSuggested<T>>(this, true, unused => LoadPage(CurrentPage));
Even though the LoadPage function was not creating a different thread manually (at that moment), it was executed in the same thread as the CommitCurrentItem. A Task is not guaranteed to run in a different thread, but in this case it was. LoadPage just called the code that was in the question. My code is guaranteed to raise the PropertyChanged event (from the INotifyPropertyChanged interface) in the UI thread, so when I set the ItemList property (of type IEnumerable), the UI was notified so it shows the new list. So, the UI retrieved the new value of ItemList while (in the other thread), the line TotalResults = count.Value; was executing.
In this case, I guess the query is not executed against the database until I retrieve the first value (the first item in the list or the RowCount).
Remember that ISession is not thread safe, so this situation was unreliable: the UI thread and the other thread was using the same session at the same time. In my code I'm not sharing the sessions between the ViewModels, and each ViewModel uses the Session with only one thread at the same time, in order to prevent this kind of situations.
So, the final solution was to force the execution of the query in the same thread I was working, so I simply called count.Value before setting ItemList to result.

Autocomplete textbox freezes while executing query. Must be a better way!

everyone! I searched the best I could and did not find exactly the help I was looking for.
Problem
AutoCompleteTextbox FREEZES and "eats" characters while query is performed
Asking for
Mimic Google Instant functionality
Background
First things first: C#, WPF, .NET 4.0
Ok, now that's out of the way, I'm trying to find the best way to implement a dynamic AutoComplete Textbox, which queries a database for results after each letter typed.
The following code gets executed when the AutoCompleteTextBox's TextChanged event is fired:
public void Execute(object sender, object parameter)
{
//removed some unnecessary code for the sake of being concise
var autoCompleteBox = sender as AutoCompleteTextBox;
var e = parameter as SearchTextEventArgs;
var result = SearchUnderlyings(e.SearchText);
autoCompleteBox.ItemsSource = result;
}
Now, let's say that SearchUnderlyings(e.SearchText) takes an average of 600-1100ms - during that time, the textbox is frozen and it "eats" any keys pressed. This is an annoying problem I've been having. For some reason, the LINQ in SearchUnderlyings(e.SearchText) is running in the GUI thread. I tried delegating this to a background thread, but still same result.
Ideally, I would like the textbox to work the way Google Instant does - but I don't want to be "killing" threads before the server/query can return a result.
Anyone have experience or can offer some guidance which will allow me to query as I type without freezing the GUI or killing the server?
Thank you guys!
This line:
var result = SearchUnderlyings(e.SearchText);
Runs synchronously, locking the UI thread. The way to cure this would be to switch to an asynchronous pattern, where you start the query, and then do something when it finishes.
This article demonstrates it pretty nicely, and shows some solutions - http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx
What is probably killing you is setting the binding source over and over again (which is why running the query on a background thread doesn't make a difference).
You might consider the algorithm as a whole. Depending on your data, you could wait until the user enters the first three characters and then do one large query against the database. Bind the item source once. Each character typed afterwards just performs a filter against your data that is already cached on the client. That way you are not hitting the database over and over (which is going to be terribly expensive).
Or consider just bringing back three or so results from the DB to keep your service serialization time down.
So, we kind of hacked something quick. By making the calls to SearchUnderlyings(e.SearchText) asynchronous, my GUI thread is no longer blocked and the textbox is no longer "eating" key presses. By adding the lastQueryTag == _lastQuery check, we are trying to ensure some thread-safety, allowing only the most recent query to set the ItemsSource.
Perhaps not the most ideal or elegant solution. I am still open to further critiques and suggestions. Thank you!
private long _lastQuery = DateTime.Now.Ticks;
public void Execute(object sender, object parameter)
{
var autoCompleteBox = sender as AutoCompleteTextBox;
var e = parameter as SearchTextEventArgs;
// removed unecessary code for clarity
long lastQueryTag = _lastQuery = DateTime.Now.Ticks;
Task.Factory.StartNew(() =>
{
var result = SearchUnderlyings(e.SearchText);
System.Windows.Application.Current.Dispatch(() =>
{
if (lastQueryTag == _lastQuery)
autoCompleteBox.ItemsSource = result;
});
});
}

Categories