-
-
Notifications
You must be signed in to change notification settings - Fork 182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: MergeManyChangeSets (New Operator for Cache ChangeSets) #736
Feature: MergeManyChangeSets (New Operator for Cache ChangeSets) #736
Conversation
…\Cache\Internal\MergeManyChangeSets.cs
Codecov Report
@@ Coverage Diff @@
## main #736 +/- ##
==========================================
+ Coverage 71.77% 72.22% +0.45%
==========================================
Files 216 217 +1
Lines 10900 11057 +157
==========================================
+ Hits 7823 7986 +163
+ Misses 3077 3071 -6
|
I ran into an issue implementing this with the overloads suggested by @RolandPheasant in #457. The overload resolution works just as he said it would, but therein is the problem. There is at least one place in this codebase that uses MergeMany with ChangeSets but does not expect the ChangeSet-aware behavior. Specifically, the unit tests for Initially, I was just going to update Instead, I removed the overloads that don't take an @RolandPheasant I'm not sure how you want to handle this. I'd really like to have the overload that doesn't require any extra parameters, so that it works just like regular Maybe we can document the addition and rev the version so that they don't just pick up a version that will break them by mistake? Or maybe we can use a slightly different name, like |
Yikes, Ill get back to you tomorrow, but MergeManyChangeSets may be the way forward. If that's the solution, it will be worth writing some anaylsers to advise when MergeMany is used instead of MergeManyChangeSets |
That's a great idea but not something I'm familiar with doing. It seems very complicated. I should have realized there was going to be a larger problem when I had to add explicit type parameters to the internal call to As far as a new name, It does make sense to define a new operator here because what is actually required is a family of operators. This PR covers the Cache ChangeSet -> Cache ChangeSet case. But there are 3 others that should be considered at some point:
|
Lets commit to using MergeManyChangeSets, and we can worry about the other permutations which you mention above as a distinct task. |
@dwcullop The good news is I am soon about to start a role where the organisation already uses Dynamic Data. This will mean I am closer to the code base and it will sure lead to me breathing some fresh life into this project, and I'll certainly be keen to fill any gaps within the operator base. This PR falls into that category, so thanks for the effort. |
You're quite welcome. I'm happy to contribute to this project. It has saved me a lot of time and energy, so it feels right to give back. Congrats on the new role! |
I've updated everything to use the name
That should allow consumers to use either, both, or neither of the comparers. I also renamed the internal implementation file to specify "Cache" so that the "List" version can be made with minimal confusion. I also realized that I had forgotten an important use-case (important for my project anyway): The Observable resulting from When adding support for this, I was surprised to learn that Originally, I was going to put it in I don't think it will break any existing users, but if you prefer, we can hide the new behavior behind a parameter (or go back to the original plan of putting it in Let me know what you think. We could also put the |
I think there are still some gaps with regard to expected behavior. This PR covers the case for transforming a changeset of It would be nice to have something like: IObservable<IChangeSet<T, TKey>> MergeChangeSets<T, TKey>(this IEnumerable<IObservable<IChangeSet<T, TKey>>> sources, ...); Or: IObservable<IChangeSet<T, TKey>> MergeChangeSets<T, TKey>(this IObservable<IChangeSet<T, TKey>> first, params IObservable<IChangeSet<T, TKey>>>[] others, ...); I guess that is more like This PR relies on the source stream having a key that is used to index the source changesets. The above wouldn't have that, so I guess that it is more like the Source List to Source Cache case (except the list is static), so maybe we should wait for that. I guess I'll work on that next because we can get the above operators for free once we have something that does Source List to Source Cache changeset merges. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ace, thanks.
Regarding the additional |
I think you're right on this one. I cannot remember for the life of me why this is not the default case. |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Description
This PR addresses #457 by adding a set of observable Cache ChangeSet-Aware operators name
MergeManyChangeSets
. These operators work just likeMergeMany
except allows the result to be ChangeSets which are then merged together, as if they came from a single source cache. It correctly handles both Remove and Update events from the source observable by removing any items added via the changeset associated with the item being removed/updated. It also handles when the same key is emitted from multiple child changesets by allowing the consumer to provide anIComparer
instance to determine priority.It was developed by cloning the code for
DynamicCombiner
, but since it only needs to handle the "Or" case, it could then be optimized for the various operations (Add/Remove/Update).Optional Parameters
Using the two overloads, the consumer can provide an
IEqualityComparer
, anIComparer
, or both (or neither).IEqualityComparer
The
IEqualityComparer
instance that is used to determine equality of items in the result changeset. An Update change will not be fired for an item if it is equal to the previously observed value for the same key.IComparer
When provided, the
IComparer
instance will be used to determine priority when multiple upstream changesets produces values with the same key. If a child changeset emits a value with the same key as previously emitted by another child changeset, the downstream will observe an update if and only if the new value has a higher priority according to the comparer (otherwise, it won't observe anything). When a value is removed from a child changeset, if it is the value that has most recently been emitted downstream, an Update event will be emitted replacing the value with the NEXT highest priority item from all of the available values for that key. If there are no available values, a Remove event will be observed downstream instead.The
IComparer
is entirely optional. If not supplied, then the first value to be observed will have priority. If that value is later removed, it will be replaced by the first available value. If the child changesets do not produce overlapping keys, there's no reason to supply this value at all.Unit Tests
There is a suite of unit tests that are based around the contrived example of having different markets having different prices for the same items and the need to aggregate the prices from all the markets using different criteria (lowest price, highest price, etc).
Note: These overloads only work on Cache ChangeSets. Supporting List ChangeSets will require a different implementation and should be a different PR.