Using ReactiveUI/DynamicData, How to observe changes to properties of items in a SourceList - reactiveui

Each item in the list implements ReactiveObject so I've tried using item.WhenAnyValue().Subscribe() on each individual item before adding it to the SourceList. While this works, it has terrible performance and isn't really practical for my SourceList of 40000 items. Is there any way to observe the SourceList once for changes to properties of any items in the list?

DynamicData provides several extensions to IObservable<IChangeSet<T>> which you can use to bind to all of the items in your SourceList. All of these will gracefully handle added/removed items from your list.
WhenValueChanged
This is basically the equivalent of WhenAnyValue but for lists and is probably what you're looking for. This will return an observable of the value when a target property has changed (and optionally when initialized). Example:
sourceList
.Connect()
.WhenValueChanged(item => item.property)
.Subscribe(newPropertyValue => { /* Do stuff */ }
WhenPropertyChanged
Similar to ObservableForProperty, this will return an observable of the value of target property and its parent object, when it has changed (and optionally when initialized). Example:
sourceList
.Connect()
.WhenPropertyChanged(item => item.property)
.Subscribe(change => { /* Do stuff with change.Sender and change.Value */ }
MergeMany
The most general option, MergeMany allows you to combine observables created or retrieved from each of your list items. For example, to accomplish something similar to WhenValueChanged you could do:
sourceList
.Connect()
.MergeMany(item => item.WhenAnyValue(x => x.property))
.Subscribe(newPropertyValue => { /* Do stuff */ }

Related

NRules—How to Build List of Array Indexes of Matches

I have two lists of objects. I want to loop though the first list and see if there are any matches in the second list, based on multiple fields, and I'd like to use a method which returns a bool to do the logic. However, I don't know if that will be possible. For the items in the first list that I'm looping through, when there is a match, I want to add the index to a list, building up a list of the indexes which match.
I know that you can look at an IEnumerable in LINQ and get back a list of indexes based on matches, but I'm not sure if that is the way to do this. For example:
myarray
.Select((p, i) => new { Item = p, Index = i })
.Where(p => SomeCondition(p.Item))
.Select(p => p.Index);
With the list of indexes that is created, I want to add those numbers to an object which will be output in the "Then()". Let me try and show this in pseudo code. In this example, let's say there are 10 objects in each of the lists, and there items 0, 2, and 7 match.
public override void Define()
{
ListOfObjects inbound; // fact in memory with 10 objects
ListOfObjects outbound; // fact in memory with 10 objects
List<int> matchingIndexes; // for storing the indexes of objects that match
OutputObject outputObject; // do store output data based on indexes
When()
.Match(() => inbound, some criteria) // get list of objects--this already works in my code
.Match(() => outbound, some other criteria) // get list of objects--this already works in my code
// Loop through inbound and for each matching item in outbound, add inbound's array index to "matchingIndexes".
// Should looping occur here or in "DoSomeLogic"? Maybe it makes sense to do all the looping and logic
// in "DoSomeLogic()" and have that return the list of indexes, but how do I assign that
// value to "matchingIndexes"?
.Match/Query/Whatever(() => matchingIndexes, DoSomeLogic(inbound, outbound))
Then()
.Do(ctx => outputObject.ProcessMatchingIndexes(matchingIndexes))
}
"matchingIndexes" should contain a list of 3 integers: 0, 2, and 7. "outputObject.ProcessMatchingIndexes" will receive that list at a parameter and do some things with those numbers.

Difference between ListResultDto and List

In my application service, why should I return ListResultDto instead of returning List?
It seems like extra work.
On the frontend, it means I need to access the items property instead of treating the results as an array e.g.
Using ListResultDto
.subscribe((next) => this.entities = next.items);
Using List
.subscrube((next) => this.entities = next);
Using ListResultDto allows you to add additional information about your items in the future without breaking your existing API. If you return items as an array, you are restricting your API.
An example is TotalCount in PagedResultDto, if you want to implement pagination of items.

C# Remove object from list stored in an ASP session

I am making a small eBook Store as a project. I am storing the cart in the ASP session as a list of objects. Now, in the checkout page, I am showing that list of objects in a list box and allowing the user to delete an item if needed. This is my code
protected void btnCart_Click(object sender, EventArgs e)
{
List<Title> cartItems = (List<Title>)Session["eStoreCart"];
int itemToRemove = Int32.Parse(lbCartItems.SelectedItem.Value);
Title ttl = ebs.Titles.SingleOrDefault(t => t.TitleId == itemToRemove);
cartItems.Remove(ttl);
Session["eStoreCart"] = cartItems;
FillListBox();
}
Apparently, the number of items in cartItems are same before and after the Remove() method is called. Where am I going wrong?
A similar method was used in Add to Card with cartItems.Add(ttl), which is working flawlessly.
Instead of
Title ttl = ebs.Titles.SingleOrDefault(t => t.TitleId == itemToRemove);
Try
Title ttl = cartItems.SingleOrDefault(t => t.TitleId == itemToRemove);
i.e. instead of searching for the Title in 'ebs' (not sure what it contains, as not clear in OP code), search for items in Session Object directly and then remove it.
The .Remove() method is going to check for equality when trying to remove the item. Does Title implement IEquatable<Title>? If not then the check for equality is going to be default, which for objects is reference equality. And it's unlikely that reference equality is being satisfied here.
Implementing IEquatable<Title> is probably the ideal approach here. Just add that interface to the Title implementation and implement its one method:
public bool Equals(Title other)
{
if (other == null)
return false;
if (this.SomeValue == other.SomeValue)
return true;
else
return false;
}
This would put the equality logic on the model where it belongs. Failing that, your code would have to be more procedural in checking for equality. That is, you'd first have to find the element in cartItems that you're looking for and then remove that object, rather than trying to remove an object which itself exists in ebs.Titles. Something like:
cartItems.Remove(cartItems.Single(c => c.SomeValue == ttl.SomeValue));
That way the code is referencing the same in-memory object, not just an object that intuitively represents the same thing.

how to sort descending order of ObservableCollection into WP7.?

I want to sort my ObservableCollection into the Descending Order I had tried with different scenario but I am not able to sort it my code is like this.
1>
LeaderboardItems = new ObservableCollection<AEGAPI.clsAEGAPI.Leaderboard>(LeaderboardItems.OrderByDescending(a => a.Points));
2>
LeaderboardItems.OrderByDescending(p => p.Points);
I had tried lots of but I am not able to get my result.
Normally you can't sort a ObservableCollection, because than you are changing the collection. If you only want to show the sorted list:
List<AEGAPI.clsAEGAPI.Leaderboard> list = LeaderboardItems.OrderByDescending(p => p.Points).ToList()
Or you can directly bind the source to a listbox
listbox.ItemsSource = LeaderboardItems.OrderByDescending(p => p.Points)
The list will not react to changes on the observable collection. You have to implement it your self.
In the past i found someone who has made a sortable observable collection, maybe you can go for that option (search for : sortableobservablecollection), but is was a bit complex.
Greets

Using Reactives to Merge Chunked Messages

So I'm attempting to use reactives to recompose chunked messages identified by ID and am having a problem terminating the final observable. I have a Message class which consists of Id, Total Size, Payload, Chunk Number and Type and have the following client-side code:
I need to calculate the number of messages to Take at runtime
(from messages in
(from messageArgs in Receive select Serializer.Deserialize<Message>(new MemoryStream(Encoding.UTF8.GetBytes(messageArgs.Message))))
group messages by messages.Id into grouped select grouped)
.Subscribe(g =>
{
var cache = new List<Message>();
g.TakeWhile((int) Math.Ceiling(MaxPayload/g.First().Size) < cache.Count)
.Subscribe(cache.Add,
_ => { /* Rebuild Message Parts From Cache */ });
});
First I create a grouped observable filtering messages by their unique ID and then I am trying to cache all messages in each group until I have collected them all, then I sort them and put them together. The above seems to block on g.First().
I need a way to calculate the number to take from the first (or any) of the messages that come through however am having difficulty doing so. Any help?
First is a blocking operator (how else can it return T and not IObservable<T>?)
I think using Scan (which builds an aggregate over time) could be what you need. Using Scan, you can hide the "state" of your message re-construction in a "builder" object.
MessageBuilder.IsComplete returns true when all the size of messages it has received reaches MaxPayload (or whatever your requirements are). MessageBuilder.Build() then returns the reconstructed message.
I've also moved your "message building" code into a SelectMany, which keeps the built messages within the monad.
(Apologies for reformatting the code into extension methods, I find it difficult to read/write mixed LINQ syntax)
Receive
.Select(messageArgs => Serializer.Deserialize<Message>(
new MemoryStream(Encoding.UTF8.GetBytes(messageArgs.Message))))
.GroupBy(message => message.Id)
.SelectMany(group =>
{
// Use the builder to "add" message parts to
return group.Scan(new MessageBuilder(), (builder, messagePart) =>
{
builder.AddPart(messagePart);
return builder;
})
.SkipWhile(builder => !builder.IsComplete)
.Select(builder => builder.Build());
})
.Subscribe(OnMessageReceived);

Resources