From 1aacde14e1ab71f4db02bfc0d66279fe15f75dc6 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Tue, 9 Jul 2019 23:16:28 +1000 Subject: [PATCH] Move files from base directory to /src directory This will help with azure devops compiling and the build recipe --- appveyor.yml | 20 +- .../Directory.Build.props | 0 .../Directory.build.targets | 0 .../Cache/SourceCache.cs | 0 .../DynamicData.Benchmarks.csproj | 0 .../DynamicData.Benchmarks}/List/GroupAdd.cs | 0 .../List/GroupRemove.cs | 0 .../List/SourceList.cs | 0 .../DynamicData.Benchmarks}/Program.cs | 0 .../DynamicData.Profile}/Allocations.cs | 0 .../DynamicData.Profile}/AllocationsCount.cs | 0 .../CacheAllocationChecks.cs | 0 .../CheapAndDirtyAllocationTest.cs | 0 .../DynamicData.Profile.csproj | 0 .../DynamicData.Profile}/Person.cs | 0 .../Domain/Person.cs | 0 .../Domain/RandomPersonGenerator.cs | 0 .../DynamicData.ReactiveUI.Tests.csproj | 0 .../Fixtures/BindChangeSetFixture.cs | 0 .../Fixtures/BindFromObservableListFixture.cs | 0 .../Fixtures/BindSortedChangeSetFixture.cs | 0 ...oObservableChangeSetWithoutINdexFixture.cs | 0 .../Fixtures/ToObservableChangeSetFixture.cs | 0 .../Fixtures/TransformManyFixture.cs | 0 ...ransformManyObservableCollectionFixture.cs | 0 ...mManyObservableCollectionWithKeyFixture.cs | 0 .../DynamicData.ReactiveUI.csproj | 0 .../DynamicData.ReactiveUI}/DynamicDataEx.cs | 0 .../ObservableCacheToReactiveListAdaptor.cs | 0 .../ObservableListToReactiveListAdaptor.cs | 0 .../DynamicData.ReactiveUI}/ReactiveListEx.cs | 0 .../SortedReactiveListAdaptor.cs | 0 .../AggregationTests/AggregationFixture.cs | 0 .../AggregationTests/AverageFixture.cs | 0 .../AggregationTests/MaxFixture.cs | 0 .../AggregationTests/MinFixture.cs | 0 .../AggregationTests/SumFixture.cs | 0 .../DynamicData.Tests}/AutoRefreshFilter.cs | 0 .../Binding/BindingLIstBindListFixture.cs | 0 .../Binding/BindingListBindCacheFixture.cs | 0 .../BindingListBindCacheSortedFixture.cs | 0 ...eeplyNestedNotifyPropertyChangedFixture.cs | 0 .../Binding/NotifyPropertyChangedExFixture.cs | 0 .../ObservableCollectionBindCacheFixture.cs | 0 ...ervableCollectionBindCacheSortedFixture.cs | 0 .../ObservableCollectionBindListFixture.cs | 0 ...bleCollectionExtendedToChangeSetFixture.cs | 0 .../ObservableCollectionToChangeSetFixture.cs | 0 ...yObservableCollectionToChangeSetFixture.cs | 0 .../DynamicData.Tests}/Cache/AndFixture.cs | 0 .../Cache/AutoRefreshFixture.cs | 0 .../DynamicData.Tests}/Cache/BatchFixture.cs | 0 .../Cache/BatchIfFixture.cs | 0 .../Cache/BatchIfWithTimeOutFixture.cs | 0 .../Cache/BufferInitialFixture.cs | 0 .../Cache/ChangesReducerFixture.cs | 0 .../Cache/DeferUntilLoadedFixture.cs | 116 +- .../Cache/DisposeManyFixture.cs | 0 .../Cache/DistinctFixture.cs | 0 .../Cache/DynamicAndFixture.cs | 0 .../Cache/DynamicExceptFixture.cs | 0 .../Cache/DynamicOrFixture.cs | 0 .../Cache/DynamicXorFixture.cs | 0 .../Cache/EditDiffFixture.cs | 0 ...eObservableToObservableChangeSetFixture.cs | 0 .../DynamicData.Tests}/Cache/ExceptFixture.cs | 0 .../Cache/ExpireAfterFixture.cs | 0 .../Cache/FilterControllerFixture.cs | 606 +-- .../DynamicData.Tests}/Cache/FilterFixture.cs | 428 +- .../Cache/FilterOnPropertyFixture.cs | 0 .../Cache/FilterParallelFixture.cs | 0 .../Cache/ForEachChangeFixture.cs | 0 .../Cache/FromAsyncFixture.cs | 0 .../Cache/FullJoinFixture.cs | 0 .../Cache/FullJoinManyFixture.cs | 0 .../Cache/GroupControllerFixture.cs | 0 .../GroupControllerForFilteredItemsFixture.cs | 0 .../DynamicData.Tests}/Cache/GroupFixture.cs | 0 .../Cache/GroupFromDistinctFixture.cs | 0 .../Cache/GroupImmutableFixture.cs | 0 .../Cache/GroupOnPropertyFixture.cs | 0 ...roupOnPropertyWithImmutableStateFixture.cs | 0 .../Cache/IgnoreUpdateFixture.cs | 0 .../Cache/IncludeUpdateFixture.cs | 0 .../Cache/InnerJoinFixture.cs | 0 .../Cache/InnerJoinManyFixture.cs | 0 .../Cache/KeyValueCollectionEx.cs | 0 .../Cache/LeftJoinFixture.cs | 0 .../Cache/LeftJoinManyFixture.cs | 0 .../Cache/MergeManyFixture.cs | 0 .../Cache/MergeManyItemsFixture.cs | 0 .../Cache/MergeManyWithKeyOverloadFixture.cs | 0 .../Cache/MonitorStatusFixture.cs | 0 .../Cache/ObservableCachePreviewFixture.cs | 0 .../Cache/ObservableChangeSetFixture.cs | 0 .../ObservableToObservableChangeSetFixture.cs | 0 .../DynamicData.Tests}/Cache/OnItemFixture.cs | 0 .../DynamicData.Tests}/Cache/OrFixture.cs | 0 .../DynamicData.Tests}/Cache/PageFixture.cs | 0 .../Cache/QueryWhenChangedFixture.cs | 0 .../Cache/RefCountFixture.cs | 0 .../Cache/RightJoinFixture.cs | 0 .../Cache/RightJoinManyFixture.cs | 0 .../Cache/SizeLimitFixture.cs | 0 .../DynamicData.Tests}/Cache/SortFixture.cs | 0 .../Cache/SortObservableFixtureFixture.cs | 0 .../Cache/SourceCacheFixture.cs | 0 .../Cache/SubscribeManyFixture.cs | 0 .../DynamicData.Tests}/Cache/SwitchFixture.cs | 0 .../Cache/TimeExpiryFixture.cs | 0 .../Cache/ToObservableChangeSetFixture.cs | 0 ...bservableChangeSetFixtureWithCompletion.cs | 0 .../Cache/ToSortedCollectionFixture.cs | 0 .../Cache/TransformAsyncFixture.cs | 542 +-- .../Cache/TransformFixture.cs | 0 .../Cache/TransformFixtureParallel.cs | 0 .../Cache/TransformManyFixture.cs | 0 .../TransformManyObservableCacheFixture.cs | 0 .../Cache/TransformManyRefreshFixture.cs | 0 .../Cache/TransformManySimpleFixture.cs | 0 .../Cache/TransformSafeAsyncFixture.cs | 580 +-- .../Cache/TransformSafeFixture.cs | 0 .../Cache/TransformSafeParallelFixture.cs | 0 .../Cache/TransformTreeFixture.cs | 0 .../Cache/TransformTreeWithRefreshFixture.cs | 0 .../Cache/TrueForAllFixture.cs | 0 .../Cache/TrueForAnyFixture.cs | 0 .../DynamicData.Tests}/Cache/WatchFixture.cs | 0 .../Cache/WatcherFixture.cs | 0 .../DynamicData.Tests}/Cache/XorFixture.cs | 0 .../DynamicData.Tests}/Domain/Animal.cs | 0 .../Domain/ParentAndChildren.cs | 0 .../DynamicData.Tests}/Domain/ParentChild.cs | 0 .../DynamicData.Tests}/Domain/Person.cs | 236 +- .../Domain/PersonEmployment.cs | 0 .../DynamicData.Tests}/Domain/PersonObs.cs | 242 +- .../Domain/PersonWithChildren.cs | 0 .../Domain/PersonWithEmployment.cs | 0 .../Domain/PersonWithFriends.cs | 0 .../Domain/PersonWithGender.cs | 0 .../Domain/PersonWithRelations.cs | 0 .../DynamicData.Tests}/Domain/Pet.cs | 0 .../Domain/RandomPersonGenerator.cs | 0 .../Domain/SelfObservingPerson.cs | 0 .../DynamicData.Tests.csproj | 46 +- .../EnumerableExFixtures.cs | 0 .../Kernal/CacheUpdaterFixture.cs | 0 .../Kernal/DistinctUpdateFixture.cs | 0 .../DynamicData.Tests}/Kernal/EnumerableEx.cs | 0 .../Kernal/KeyValueFixture.cs | 0 .../Kernal/OptionFixture.cs | 0 .../Kernal/SourceUpdaterFixture.cs | 286 +- .../Kernal/UpdateFixture.cs | 0 .../DynamicData.Tests}/List/AndFixture.cs | 0 .../List/AutoRefreshFixture.cs | 958 ++-- .../DynamicData.Tests}/List/BatchFixture.cs | 0 .../DynamicData.Tests}/List/BatchIfFixture.cs | 0 .../List/BatchIfWithTimeOutFixture.cs | 0 .../DynamicData.Tests}/List/BufferFixture.cs | 0 .../List/BufferInitialFixture.cs | 0 .../DynamicData.Tests}/List/CastFixture.cs | 0 .../List/ChangeAwareListFixture.cs | 0 .../List/CloneChangesFixture.cs | 0 .../DynamicData.Tests}/List/CloneFixture.cs | 0 .../List/CreationFixtures.cs | 98 +- .../List/DeferUntilLoadedFixture.cs | 0 .../List/DisposeManyFixture.cs | 0 .../List/DistinctValuesFixture.cs | 0 .../List/DynamicAndFixture.cs | 0 .../List/DynamicExceptFixture.cs | 0 .../List/DynamicOrFixture.cs | 0 .../List/DynamicXOrFixture.cs | 0 .../List/EditDiffFixture.cs | 0 .../DynamicData.Tests}/List/ExceptFixture.cs | 0 .../List/ExpireAfterFixture.cs | 0 ...terControllerFixtureWithClearAndReplace.cs | 0 .../FilterControllerFixtureWithDiffSet.cs | 0 .../DynamicData.Tests}/List/FilterFixture.cs | 0 .../List/FilterOnObservableFixture.cs | 0 .../List/FilterOnPropertyFixture.cs | 0 .../List/FilterWithObservable.cs | 0 .../List/ForEachChangeFixture.cs | 0 .../List/FromAsyncFixture.cs | 0 .../List/GroupImmutableFixture.cs | 0 .../DynamicData.Tests}/List/GroupOnFixture.cs | 0 .../List/GroupOnPropertyFixture.cs | 0 ...roupOnPropertyWithImmutableStateFixture.cs | 0 .../List/MergeManyChangeSetsFixture.cs | 0 .../List/MergeManyFixture.cs | 0 .../DynamicData.Tests}/List/OrFixture.cs | 0 .../DynamicData.Tests}/List/PageFixture.cs | 0 .../List/QueryWhenChangedFixture.cs | 0 .../List/RecursiveTransformManyFixture.cs | 0 .../List/RefCountFixture.cs | 0 .../List/RemoveManyFixture.cs | 0 .../DynamicData.Tests}/List/ReverseFixture.cs | 0 .../DynamicData.Tests}/List/SelectFixture.cs | 0 .../List/SizeLimitFixture.cs | 0 .../DynamicData.Tests}/List/SortFixture.cs | 0 .../List/SortMutableFixture.cs | 0 .../List/SortPrimitiveFixture.cs | 0 .../List/SourceListPreviewFixture.cs | 0 .../List/SubscribeManyFixture.cs | 0 .../DynamicData.Tests}/List/SwitchFixture.cs | 0 .../List/ToObservableChangeSetFixture.cs | 0 .../List/TransformAsyncFixture.cs | 248 +- .../List/TransformFixture.cs | 0 .../List/TransformManyFixture.cs | 0 ...ransformManyObservableCollectionFixture.cs | 0 .../List/TransformManyRefreshFixture.cs | 0 .../List/VirtualisationFixture.cs | 0 .../DynamicData.Tests}/List/XOrFixture.cs | 0 .../Utilities/SelectManyExtensions.cs | 0 DynamicData.sln => src/DynamicData.sln | 280 +- .../Aggregation/AggregateEnumerator.cs | 0 .../DynamicData}/Aggregation/AggregateItem.cs | 0 .../DynamicData}/Aggregation/AggregateType.cs | 0 .../DynamicData}/Aggregation/AggregationEx.cs | 0 .../DynamicData}/Aggregation/Avg.cs | 0 .../DynamicData}/Aggregation/AvgEx.cs | 0 .../DynamicData}/Aggregation/CountEx.cs | 0 .../Aggregation/IAggregateChangeSet.cs | 0 .../DynamicData}/Aggregation/MaxEx.cs | 0 .../DynamicData}/Aggregation/StdDev.cs | 0 .../DynamicData}/Aggregation/StdDevEx.cs | 0 .../DynamicData}/Aggregation/SumEx.cs | 0 .../Alias/ObservableCacheAlias.cs | 0 .../DynamicData}/Alias/ObservableListAlias.cs | 0 .../DynamicData}/Attributes.cs | 0 .../Binding/AbstractNotifyPropertyChanged.cs | 0 .../Binding/BindingListAdaptor.cs | 0 .../Binding/BindingListEventsSuspender.cs | 0 .../DynamicData}/Binding/ExpressionBuilder.cs | 0 .../DynamicData}/Binding/IEvaluateAware.cs | 0 .../DynamicData}/Binding/IIndexAware.cs | 0 .../INotifyCollectionChangedSuspender.cs | 0 .../Binding/IObservableCollection.cs | 0 .../Binding/IObservableCollectionAdaptor.cs | 0 .../ISortedObservableCollectionAdaptor.cs | 0 .../Binding/NotifyPropertyChangedEx.cs | 0 .../Binding/ObservableCollectionAdaptor.cs | 0 .../Binding/ObservableCollectionEx.cs | 0 .../Binding/ObservableCollectionExtended.cs | 0 .../Binding/ObservablePropertyFactory.cs | 0 .../Binding/ObservablePropertyFactoryCache.cs | 0 .../Binding/ObservablePropertyPart.cs | 0 .../DynamicData}/Binding/PropertyValue.cs | 0 .../DynamicData}/Binding/SortDirection.cs | 0 .../DynamicData}/Binding/SortExpression.cs | 0 .../Binding/SortExpressionComparer.cs | 0 .../Binding/SortedBindingListAdaptor.cs | 0 .../SortedObservableCollectionAdaptor.cs | 0 .../DynamicData}/Cache/Change.cs | 0 .../DynamicData}/Cache/ChangeAwareCache.cs | 0 .../DynamicData}/Cache/ChangeReason.cs | 0 .../DynamicData}/Cache/ChangeSet.cs | 0 .../DynamicData}/Cache/DistinctChangeSet.cs | 0 .../DynamicData}/Cache/GroupChangeSet.cs | 0 .../DynamicData}/Cache/ICache.cs | 0 .../DynamicData}/Cache/ICacheUpdater.cs | 0 .../DynamicData}/Cache/IChangeSet.cs | 0 .../DynamicData}/Cache/IChangeSetAdaptor.cs | 0 .../DynamicData}/Cache/IDistinctChangeSet.cs | 0 .../DynamicData}/Cache/IGroup.cs | 0 .../DynamicData}/Cache/IGroupChangeSet.cs | 0 .../DynamicData}/Cache/IGrouping.cs | 0 .../Cache/IImmutableGroupChangeSet.cs | 0 .../DynamicData}/Cache/IIntermediateCache.cs | 0 .../DynamicData}/Cache/IKeyValue.cs | 0 .../DynamicData}/Cache/IKeyValueCollection.cs | 0 .../DynamicData}/Cache/IObservableCache.cs | 0 .../DynamicData}/Cache/IPageRequest.cs | 0 .../DynamicData}/Cache/IPageResponse.cs | 0 .../DynamicData}/Cache/IPagedChangeSet.cs | 0 .../DynamicData}/Cache/IQuery.cs | 0 .../DynamicData}/Cache/ISortedChangeSet.cs | 0 .../Cache/ISortedChangeSetAdaptor.cs | 0 .../DynamicData}/Cache/ISourceCache.cs | 0 .../DynamicData}/Cache/ISourceUpdater.cs | 0 .../DynamicData}/Cache/IVirtualChangeSet.cs | 0 .../DynamicData}/Cache/IVirtualParameters.cs | 0 .../DynamicData}/Cache/IVirtualRequest.cs | 0 .../DynamicData}/Cache/IndexedItem.cs | 0 .../DynamicData}/Cache/IntermediateCache.cs | 0 .../Cache/Internal/AbstractFilter.cs | 0 .../Internal/AnonymousObservableCache.cs | 0 .../Cache/Internal/AnonymousQuery.cs | 0 .../Cache/Internal/AutoRefresh.cs | 0 .../DynamicData}/Cache/Internal/BatchIf.cs | 0 .../DynamicData}/Cache/Internal/Cache.cs | 0 .../DynamicData}/Cache/Internal/CacheEx.cs | 0 .../Cache/Internal/CacheUpdater.cs | 0 .../DynamicData}/Cache/Internal/Cast.cs | 0 .../Cache/Internal/ChangesReducer.cs | 0 .../Cache/Internal/CombineOperator.cs | 0 .../DynamicData}/Cache/Internal/Combiner.cs | 0 .../Cache/Internal/DeferUntilLoaded.cs | 0 .../Cache/Internal/DictionaryExtensions.cs | 0 .../Cache/Internal/DisposeMany.cs | 0 .../Cache/Internal/DistinctCalculator.cs | 0 .../Cache/Internal/DynamicCombiner.cs | 0 .../Cache/Internal/DynamicFilter.cs | 0 .../DynamicData}/Cache/Internal/EditDiff.cs | 0 .../Cache/Internal/ExpirableItem.cs | 0 .../DynamicData}/Cache/Internal/FilterEx.cs | 0 .../Cache/Internal/FilterOnProperty.cs | 0 .../Cache/Internal/FilteredIndexCalculator.cs | 0 .../Cache/Internal/FinallySafe.cs | 0 .../DynamicData}/Cache/Internal/FullJoin.cs | 0 .../Cache/Internal/FullJoinMany.cs | 0 .../Cache/Internal/GroupImmutable.cs | 0 .../DynamicData}/Cache/Internal/GroupOn.cs | 0 .../Cache/Internal/GroupOnProperty.cs | 0 .../GroupOnPropertyWithImmutableState.cs | 0 .../DynamicData}/Cache/Internal/IFilter.cs | 0 .../Cache/Internal/IKeySelector.cs | 0 .../Cache/Internal/ImmutableGroup.cs | 0 .../Cache/Internal/ImmutableGroupChangeSet.cs | 0 .../Cache/Internal/IndexAndNode.cs | 0 .../Cache/Internal/IndexCalculator.cs | 0 .../DynamicData}/Cache/Internal/InnerJoin.cs | 0 .../Cache/Internal/InnerJoinMany.cs | 0 .../Cache/Internal/KeyComparer.cs | 0 .../Cache/Internal/KeySelector.cs | 0 .../Cache/Internal/KeySelectorException.cs | 0 .../Cache/Internal/KeyValueCollection.cs | 0 .../Cache/Internal/KeyValueComparer.cs | 0 .../DynamicData}/Cache/Internal/LeftJoin.cs | 0 .../Cache/Internal/LeftJoinMany.cs | 0 .../Cache/Internal/LockFreeObservableCache.cs | 0 .../Cache/Internal/ManagedGroup.cs | 0 .../DynamicData}/Cache/Internal/MergeMany.cs | 0 .../Cache/Internal/MergeManyItems.cs | 0 .../Cache/Internal/ObservableWithValue.cs | 0 .../DynamicData}/Cache/Internal/Page.cs | 0 .../Cache/Internal/QueryWhenChanged.cs | 0 .../Cache/Internal/ReaderWriter.cs | 0 .../DynamicData}/Cache/Internal/RefCount.cs | 0 .../Cache/Internal/RemoveKeyEnumerator.cs | 0 .../DynamicData}/Cache/Internal/RightJoin.cs | 0 .../Cache/Internal/RightJoinMany.cs | 0 .../Cache/Internal/SizeExpirer.cs | 0 .../Cache/Internal/SizeLimiter.cs | 0 .../DynamicData}/Cache/Internal/Sort.cs | 0 .../Cache/Internal/SpecifiedGrouper.cs | 0 .../Cache/Internal/StaticFilter.cs | 0 .../Cache/Internal/StatusMonitor.cs | 0 .../Cache/Internal/SubscribeMany.cs | 0 .../DynamicData}/Cache/Internal/Switch.cs | 0 .../Cache/Internal/TimeExpirer.cs | 0 .../Cache/Internal/ToObservableChangeSet.cs | 0 .../DynamicData}/Cache/Internal/Transform.cs | 0 .../Cache/Internal/TransformAsync.cs | 0 .../Cache/Internal/TransformMany.cs | 0 .../Internal/TransformWithForcedTransform.cs | 0 .../Cache/Internal/TreeBuilder.cs | 0 .../DynamicData}/Cache/Internal/TrueFor.cs | 0 .../Cache/Internal/Virtualiser.cs | 0 .../DynamicData}/Cache/MissingKeyException.cs | 0 .../DynamicData}/Cache/Node.cs | 342 +- .../DynamicData}/Cache/ObservableCache.cs | 0 .../DynamicData}/Cache/ObservableCacheEx.cs | 0 .../DynamicData}/Cache/PageRequest.cs | 0 .../DynamicData}/Cache/PageResponse.cs | 0 .../DynamicData}/Cache/PagedChangeSet.cs | 0 .../DynamicData}/Cache/SortOptimisations.cs | 0 .../DynamicData}/Cache/SortReason.cs | 0 .../DynamicData}/Cache/SortedChangeSet.cs | 0 .../DynamicData}/Cache/SourceCache.cs | 128 +- .../DynamicData}/Cache/SourceCacheEx.cs | 0 .../Cache/Tests/ChangeSetAggregator.cs | 0 .../Tests/DistinctChangeSetAggregator.cs | 0 .../Cache/Tests/PagedChangeSetAggregator.cs | 0 .../Cache/Tests/SortedChangeSetAggregator.cs | 0 .../DynamicData}/Cache/Tests/TestEx.cs | 0 .../Cache/Tests/VirtualChangeSetAggregator.cs | 0 .../DynamicData}/Cache/VirtualChangeSet.cs | 0 .../DynamicData}/Cache/VirtualRequest.cs | 0 .../DynamicData}/Cache/VirtualResponse.cs | 0 .../Diagnostics/ChangeStatistics.cs | 0 .../DynamicData}/Diagnostics/ChangeSummary.cs | 0 .../Diagnostics/DiagnosticOperators.cs | 0 .../DynamicData}/DynamicData.csproj | 0 .../DynamicData.csproj.DotSettings | 0 .../DynamicData}/EnumerableEx.cs | 0 .../Experimental/ExperimentalEx.cs | 0 .../Experimental/ISubjectWithRefCount.cs | 0 .../DynamicData}/Experimental/IWatcher.cs | 0 .../Experimental/SubjectWithRefCount.cs | 0 .../DynamicData}/Experimental/Watcher.cs | 0 .../DynamicData}/IChangeSet.cs | 0 .../DynamicData}/Kernel/ConnectionStatus.cs | 0 .../DynamicData}/Kernel/DoubleCheck.cs | 0 .../DynamicData}/Kernel/EnumerableEx.cs | 0 .../DynamicData}/Kernel/EnumerableIList.cs | 0 .../DynamicData}/Kernel/Error.cs | 0 .../DynamicData}/Kernel/ISupportsCapcity.cs | 0 .../DynamicData}/Kernel/InternalEx.cs | 0 .../DynamicData}/Kernel/ItemWithIndex.cs | 0 .../DynamicData}/Kernel/ItemWithValue.cs | 0 .../DynamicData}/Kernel/OptionElse.cs | 0 .../DynamicData}/Kernel/OptionExtensions.cs | 0 .../DynamicData}/Kernel/Optional.cs | 0 .../DynamicData}/Kernel/ParallelEx.cs | 0 .../Kernel/ReadOnlyCollectionLight.cs | 0 .../Kernel/ReferenceEqualityComparer.cs | 0 .../DynamicData}/List/Change.cs | 0 .../DynamicData}/List/ChangeAwareList.cs | 0 .../List/ChangeAwareListWithRefCounts.cs | 0 .../DynamicData}/List/ChangeSet.cs | 0 .../DynamicData}/List/ChangeSetEx.cs | 0 .../DynamicData}/List/ChangeType.cs | 0 .../DynamicData}/List/IChangeSet.cs | 0 .../DynamicData}/List/IChangeSetAdaptor.cs | 0 .../DynamicData}/List/IExtendedList.cs | 0 .../DynamicData}/List/IGroup.cs | 0 .../DynamicData}/List/IGrouping.cs | 0 .../DynamicData}/List/IObservableList.cs | 0 .../DynamicData}/List/IPageChangeSet.cs | 0 .../DynamicData}/List/ISourceList.cs | 0 .../DynamicData}/List/IVirtualChangeSet.cs | 0 .../List/Internal/AnonymousObservableList.cs | 0 .../DynamicData}/List/Internal/AutoRefresh.cs | 0 .../DynamicData}/List/Internal/BufferIf.cs | 0 .../DynamicData}/List/Internal/Combiner.cs | 0 .../List/Internal/DeferUntilLoaded.cs | 0 .../DynamicData}/List/Internal/Distinct.cs | 0 .../List/Internal/DynamicCombiner.cs | 0 .../DynamicData}/List/Internal/EditDiff.cs | 0 .../List/Internal/ExpirableItem.cs | 0 .../DynamicData}/List/Internal/ExpireAfter.cs | 0 .../DynamicData}/List/Internal/Filter.cs | 580 +-- .../List/Internal/FilterOnObservable.cs | 0 .../List/Internal/FilterOnProperty.cs | 0 .../List/Internal/FilterStatic.cs | 0 .../DynamicData}/List/Internal/Group.cs | 0 .../DynamicData}/List/Internal/GroupOn.cs | 0 .../List/Internal/GroupOnImmutable.cs | 0 .../List/Internal/GroupOnProperty.cs | 0 .../GroupOnPropertyWithImmutableState.cs | 0 .../List/Internal/ImmutableGroup.cs | 0 .../DynamicData}/List/Internal/MergeMany.cs | 0 .../List/Internal/OnBeingAdded.cs | 0 .../List/Internal/OnBeingRemoved.cs | 0 .../DynamicData}/List/Internal/Pager.cs | 0 .../List/Internal/QueryWhenChanged.cs | 0 .../List/Internal/ReaderWriter.cs | 0 .../DynamicData}/List/Internal/RefCount.cs | 0 .../List/Internal/ReferenceCountTracker.cs | 0 .../DynamicData}/List/Internal/SizeLimiter.cs | 0 .../DynamicData}/List/Internal/Sort.cs | 0 .../List/Internal/SubscribeMany.cs | 0 .../DynamicData}/List/Internal/Switch.cs | 0 .../List/Internal/ToObservableChangeSet.cs | 0 .../List/Internal/TransformAsync.cs | 0 .../List/Internal/TransformMany.cs | 0 .../DynamicData}/List/Internal/Transformer.cs | 0 .../List/Internal/UnifiedChange.cs | 0 .../DynamicData}/List/Internal/Virtualiser.cs | 0 .../DynamicData}/List/ItemChange.cs | 318 +- .../List/Linq/AddKeyEnumerator.cs | 0 .../List/Linq/ItemChangeEnumerator.cs | 0 .../List/Linq/ReverseEnumerator.cs | 0 .../List/Linq/UnifiedChangeEnumerator.cs | 0 .../List/Linq/WithoutIndexEnumerator.cs | 0 .../DynamicData}/List/ListChangeReason.cs | 0 .../DynamicData}/List/ListEx.cs | 0 .../DynamicData}/List/ListFilterPolicy.cs | 0 .../DynamicData}/List/ObservableListEx.cs | 4084 ++++++++--------- .../DynamicData}/List/PageChangeSet.cs | 0 .../DynamicData}/List/RangeChange.cs | 0 .../DynamicData}/List/SortException.cs | 0 .../DynamicData}/List/SortOptions.cs | 0 .../DynamicData}/List/SourceList.cs | 360 +- .../List/SourceListEditConvenienceEx.cs | 0 .../DynamicData}/List/SourceListEx.cs | 0 .../List/Tests/ChangeSetAggregator.cs | 0 .../DynamicData}/List/Tests/TestEx.cs | 0 .../List/UnspecifiedIndexException.cs | 0 .../DynamicData}/List/VirtualChangeSet.cs | 0 .../DynamicData}/ObservableChangeSet.cs | 874 ++-- .../DynamicData}/ObsoleteEx.cs | 0 .../Platforms/net45/PLinqFilteredUpdater.cs | 0 .../Platforms/net45/PSubscribeMany.cs | 0 .../Platforms/net45/PTransform.cs | 0 .../Platforms/net45/ParallelEx.cs | 0 .../Platforms/net45/ParallelOperators.cs | 0 .../Platforms/net45/ParallelType.cs | 0 .../Platforms/net45/ParallelisationOptions.cs | 0 .../DynamicData}/Properties/Annotations.cs | 0 global.json => src/global.json | 0 491 files changed, 5686 insertions(+), 5686 deletions(-) rename Directory.Build.props => src/Directory.Build.props (100%) rename Directory.build.targets => src/Directory.build.targets (100%) rename {DynamicData.Benchmarks => src/DynamicData.Benchmarks}/Cache/SourceCache.cs (100%) rename {DynamicData.Benchmarks => src/DynamicData.Benchmarks}/DynamicData.Benchmarks.csproj (100%) rename {DynamicData.Benchmarks => src/DynamicData.Benchmarks}/List/GroupAdd.cs (100%) rename {DynamicData.Benchmarks => src/DynamicData.Benchmarks}/List/GroupRemove.cs (100%) rename {DynamicData.Benchmarks => src/DynamicData.Benchmarks}/List/SourceList.cs (100%) rename {DynamicData.Benchmarks => src/DynamicData.Benchmarks}/Program.cs (100%) rename {DynamicData.Profile => src/DynamicData.Profile}/Allocations.cs (100%) rename {DynamicData.Profile => src/DynamicData.Profile}/AllocationsCount.cs (100%) rename {DynamicData.Profile => src/DynamicData.Profile}/CacheAllocationChecks.cs (100%) rename {DynamicData.Profile => src/DynamicData.Profile}/CheapAndDirtyAllocationTest.cs (100%) rename {DynamicData.Profile => src/DynamicData.Profile}/DynamicData.Profile.csproj (100%) rename {DynamicData.Profile => src/DynamicData.Profile}/Person.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Domain/Person.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Domain/RandomPersonGenerator.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/DynamicData.ReactiveUI.Tests.csproj (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Fixtures/BindChangeSetFixture.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Fixtures/BindFromObservableListFixture.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Fixtures/BindSortedChangeSetFixture.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Fixtures/ToObservableChangeSetFixture.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Fixtures/TransformManyFixture.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Fixtures/TransformManyObservableCollectionFixture.cs (100%) rename {DynamicData.ReactiveUI.Tests => src/DynamicData.ReactiveUI.Tests}/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs (100%) rename {DynamicData.ReactiveUI => src/DynamicData.ReactiveUI}/DynamicData.ReactiveUI.csproj (100%) rename {DynamicData.ReactiveUI => src/DynamicData.ReactiveUI}/DynamicDataEx.cs (100%) rename {DynamicData.ReactiveUI => src/DynamicData.ReactiveUI}/ObservableCacheToReactiveListAdaptor.cs (100%) rename {DynamicData.ReactiveUI => src/DynamicData.ReactiveUI}/ObservableListToReactiveListAdaptor.cs (100%) rename {DynamicData.ReactiveUI => src/DynamicData.ReactiveUI}/ReactiveListEx.cs (100%) rename {DynamicData.ReactiveUI => src/DynamicData.ReactiveUI}/SortedReactiveListAdaptor.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/AggregationTests/AggregationFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/AggregationTests/AverageFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/AggregationTests/MaxFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/AggregationTests/MinFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/AggregationTests/SumFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/AutoRefreshFilter.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/BindingLIstBindListFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/BindingListBindCacheFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/BindingListBindCacheSortedFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/NotifyPropertyChangedExFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/ObservableCollectionBindCacheFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/ObservableCollectionBindCacheSortedFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/ObservableCollectionBindListFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/ObservableCollectionExtendedToChangeSetFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/ObservableCollectionToChangeSetFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/AndFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/AutoRefreshFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/BatchFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/BatchIfFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/BatchIfWithTimeOutFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/BufferInitialFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ChangesReducerFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/DeferUntilLoadedFixture.cs (96%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/DisposeManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/DistinctFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/DynamicAndFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/DynamicExceptFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/DynamicOrFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/DynamicXorFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/EditDiffFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/EnumerableObservableToObservableChangeSetFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ExceptFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ExpireAfterFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/FilterControllerFixture.cs (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/FilterFixture.cs (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/FilterOnPropertyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/FilterParallelFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ForEachChangeFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/FromAsyncFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/FullJoinFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/FullJoinManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/GroupControllerFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/GroupControllerForFilteredItemsFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/GroupFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/GroupFromDistinctFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/GroupImmutableFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/GroupOnPropertyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/GroupOnPropertyWithImmutableStateFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/IgnoreUpdateFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/IncludeUpdateFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/InnerJoinFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/InnerJoinManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/KeyValueCollectionEx.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/LeftJoinFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/LeftJoinManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/MergeManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/MergeManyItemsFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/MergeManyWithKeyOverloadFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/MonitorStatusFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ObservableCachePreviewFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ObservableChangeSetFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ObservableToObservableChangeSetFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/OnItemFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/OrFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/PageFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/QueryWhenChangedFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/RefCountFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/RightJoinFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/RightJoinManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/SizeLimitFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/SortFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/SortObservableFixtureFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/SourceCacheFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/SubscribeManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/SwitchFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TimeExpiryFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ToObservableChangeSetFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ToObservableChangeSetFixtureWithCompletion.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/ToSortedCollectionFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformAsyncFixture.cs (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformFixtureParallel.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformManyObservableCacheFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformManyRefreshFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformManySimpleFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformSafeAsyncFixture.cs (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformSafeFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformSafeParallelFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformTreeFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TransformTreeWithRefreshFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TrueForAllFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/TrueForAnyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/WatchFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/WatcherFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Cache/XorFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/Animal.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/ParentAndChildren.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/ParentChild.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/Person.cs (96%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/PersonEmployment.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/PersonObs.cs (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/PersonWithChildren.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/PersonWithEmployment.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/PersonWithFriends.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/PersonWithGender.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/PersonWithRelations.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/Pet.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/RandomPersonGenerator.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Domain/SelfObservingPerson.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/DynamicData.Tests.csproj (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/EnumerableExFixtures.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Kernal/CacheUpdaterFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Kernal/DistinctUpdateFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Kernal/EnumerableEx.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Kernal/KeyValueFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Kernal/OptionFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Kernal/SourceUpdaterFixture.cs (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/Kernal/UpdateFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/AndFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/AutoRefreshFixture.cs (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/BatchFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/BatchIfFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/BatchIfWithTimeOutFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/BufferFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/BufferInitialFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/CastFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/ChangeAwareListFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/CloneChangesFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/CloneFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/CreationFixtures.cs (96%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/DeferUntilLoadedFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/DisposeManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/DistinctValuesFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/DynamicAndFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/DynamicExceptFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/DynamicOrFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/DynamicXOrFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/EditDiffFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/ExceptFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/ExpireAfterFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/FilterControllerFixtureWithClearAndReplace.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/FilterControllerFixtureWithDiffSet.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/FilterFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/FilterOnObservableFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/FilterOnPropertyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/FilterWithObservable.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/ForEachChangeFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/FromAsyncFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/GroupImmutableFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/GroupOnFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/GroupOnPropertyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/GroupOnPropertyWithImmutableStateFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/MergeManyChangeSetsFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/MergeManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/OrFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/PageFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/QueryWhenChangedFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/RecursiveTransformManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/RefCountFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/RemoveManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/ReverseFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/SelectFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/SizeLimitFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/SortFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/SortMutableFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/SortPrimitiveFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/SourceListPreviewFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/SubscribeManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/SwitchFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/ToObservableChangeSetFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/TransformAsyncFixture.cs (97%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/TransformFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/TransformManyFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/TransformManyObservableCollectionFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/TransformManyRefreshFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/VirtualisationFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/List/XOrFixture.cs (100%) rename {DynamicData.Tests => src/DynamicData.Tests}/Utilities/SelectManyExtensions.cs (100%) rename DynamicData.sln => src/DynamicData.sln (98%) rename {DynamicData => src/DynamicData}/Aggregation/AggregateEnumerator.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/AggregateItem.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/AggregateType.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/AggregationEx.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/Avg.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/AvgEx.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/CountEx.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/IAggregateChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/MaxEx.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/StdDev.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/StdDevEx.cs (100%) rename {DynamicData => src/DynamicData}/Aggregation/SumEx.cs (100%) rename {DynamicData => src/DynamicData}/Alias/ObservableCacheAlias.cs (100%) rename {DynamicData => src/DynamicData}/Alias/ObservableListAlias.cs (100%) rename {DynamicData => src/DynamicData}/Attributes.cs (100%) rename {DynamicData => src/DynamicData}/Binding/AbstractNotifyPropertyChanged.cs (100%) rename {DynamicData => src/DynamicData}/Binding/BindingListAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/Binding/BindingListEventsSuspender.cs (100%) rename {DynamicData => src/DynamicData}/Binding/ExpressionBuilder.cs (100%) rename {DynamicData => src/DynamicData}/Binding/IEvaluateAware.cs (100%) rename {DynamicData => src/DynamicData}/Binding/IIndexAware.cs (100%) rename {DynamicData => src/DynamicData}/Binding/INotifyCollectionChangedSuspender.cs (100%) rename {DynamicData => src/DynamicData}/Binding/IObservableCollection.cs (100%) rename {DynamicData => src/DynamicData}/Binding/IObservableCollectionAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/Binding/ISortedObservableCollectionAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/Binding/NotifyPropertyChangedEx.cs (100%) rename {DynamicData => src/DynamicData}/Binding/ObservableCollectionAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/Binding/ObservableCollectionEx.cs (100%) rename {DynamicData => src/DynamicData}/Binding/ObservableCollectionExtended.cs (100%) rename {DynamicData => src/DynamicData}/Binding/ObservablePropertyFactory.cs (100%) rename {DynamicData => src/DynamicData}/Binding/ObservablePropertyFactoryCache.cs (100%) rename {DynamicData => src/DynamicData}/Binding/ObservablePropertyPart.cs (100%) rename {DynamicData => src/DynamicData}/Binding/PropertyValue.cs (100%) rename {DynamicData => src/DynamicData}/Binding/SortDirection.cs (100%) rename {DynamicData => src/DynamicData}/Binding/SortExpression.cs (100%) rename {DynamicData => src/DynamicData}/Binding/SortExpressionComparer.cs (100%) rename {DynamicData => src/DynamicData}/Binding/SortedBindingListAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/Binding/SortedObservableCollectionAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Change.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ChangeAwareCache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ChangeReason.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/DistinctChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/GroupChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ICache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ICacheUpdater.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IChangeSetAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IDistinctChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IGroup.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IGroupChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IGrouping.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IImmutableGroupChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IIntermediateCache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IKeyValue.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IKeyValueCollection.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IObservableCache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IPageRequest.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IPageResponse.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IPagedChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IQuery.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ISortedChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ISortedChangeSetAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ISourceCache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ISourceUpdater.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IVirtualChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IVirtualParameters.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IVirtualRequest.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IndexedItem.cs (100%) rename {DynamicData => src/DynamicData}/Cache/IntermediateCache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/AbstractFilter.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/AnonymousObservableCache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/AnonymousQuery.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/AutoRefresh.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/BatchIf.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/Cache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/CacheEx.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/CacheUpdater.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/Cast.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/ChangesReducer.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/CombineOperator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/Combiner.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/DeferUntilLoaded.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/DictionaryExtensions.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/DisposeMany.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/DistinctCalculator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/DynamicCombiner.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/DynamicFilter.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/EditDiff.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/ExpirableItem.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/FilterEx.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/FilterOnProperty.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/FilteredIndexCalculator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/FinallySafe.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/FullJoin.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/FullJoinMany.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/GroupImmutable.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/GroupOn.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/GroupOnProperty.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/GroupOnPropertyWithImmutableState.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/IFilter.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/IKeySelector.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/ImmutableGroup.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/ImmutableGroupChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/IndexAndNode.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/IndexCalculator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/InnerJoin.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/InnerJoinMany.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/KeyComparer.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/KeySelector.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/KeySelectorException.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/KeyValueCollection.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/KeyValueComparer.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/LeftJoin.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/LeftJoinMany.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/LockFreeObservableCache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/ManagedGroup.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/MergeMany.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/MergeManyItems.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/ObservableWithValue.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/Page.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/QueryWhenChanged.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/ReaderWriter.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/RefCount.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/RemoveKeyEnumerator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/RightJoin.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/RightJoinMany.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/SizeExpirer.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/SizeLimiter.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/Sort.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/SpecifiedGrouper.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/StaticFilter.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/StatusMonitor.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/SubscribeMany.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/Switch.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/TimeExpirer.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/ToObservableChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/Transform.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/TransformAsync.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/TransformMany.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/TransformWithForcedTransform.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/TreeBuilder.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/TrueFor.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Internal/Virtualiser.cs (100%) rename {DynamicData => src/DynamicData}/Cache/MissingKeyException.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Node.cs (97%) rename {DynamicData => src/DynamicData}/Cache/ObservableCache.cs (100%) rename {DynamicData => src/DynamicData}/Cache/ObservableCacheEx.cs (100%) rename {DynamicData => src/DynamicData}/Cache/PageRequest.cs (100%) rename {DynamicData => src/DynamicData}/Cache/PageResponse.cs (100%) rename {DynamicData => src/DynamicData}/Cache/PagedChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/SortOptimisations.cs (100%) rename {DynamicData => src/DynamicData}/Cache/SortReason.cs (100%) rename {DynamicData => src/DynamicData}/Cache/SortedChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/SourceCache.cs (97%) rename {DynamicData => src/DynamicData}/Cache/SourceCacheEx.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Tests/ChangeSetAggregator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Tests/DistinctChangeSetAggregator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Tests/PagedChangeSetAggregator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Tests/SortedChangeSetAggregator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Tests/TestEx.cs (100%) rename {DynamicData => src/DynamicData}/Cache/Tests/VirtualChangeSetAggregator.cs (100%) rename {DynamicData => src/DynamicData}/Cache/VirtualChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Cache/VirtualRequest.cs (100%) rename {DynamicData => src/DynamicData}/Cache/VirtualResponse.cs (100%) rename {DynamicData => src/DynamicData}/Diagnostics/ChangeStatistics.cs (100%) rename {DynamicData => src/DynamicData}/Diagnostics/ChangeSummary.cs (100%) rename {DynamicData => src/DynamicData}/Diagnostics/DiagnosticOperators.cs (100%) rename {DynamicData => src/DynamicData}/DynamicData.csproj (100%) rename {DynamicData => src/DynamicData}/DynamicData.csproj.DotSettings (100%) rename {DynamicData => src/DynamicData}/EnumerableEx.cs (100%) rename {DynamicData => src/DynamicData}/Experimental/ExperimentalEx.cs (100%) rename {DynamicData => src/DynamicData}/Experimental/ISubjectWithRefCount.cs (100%) rename {DynamicData => src/DynamicData}/Experimental/IWatcher.cs (100%) rename {DynamicData => src/DynamicData}/Experimental/SubjectWithRefCount.cs (100%) rename {DynamicData => src/DynamicData}/Experimental/Watcher.cs (100%) rename {DynamicData => src/DynamicData}/IChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/ConnectionStatus.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/DoubleCheck.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/EnumerableEx.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/EnumerableIList.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/Error.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/ISupportsCapcity.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/InternalEx.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/ItemWithIndex.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/ItemWithValue.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/OptionElse.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/OptionExtensions.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/Optional.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/ParallelEx.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/ReadOnlyCollectionLight.cs (100%) rename {DynamicData => src/DynamicData}/Kernel/ReferenceEqualityComparer.cs (100%) rename {DynamicData => src/DynamicData}/List/Change.cs (100%) rename {DynamicData => src/DynamicData}/List/ChangeAwareList.cs (100%) rename {DynamicData => src/DynamicData}/List/ChangeAwareListWithRefCounts.cs (100%) rename {DynamicData => src/DynamicData}/List/ChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/List/ChangeSetEx.cs (100%) rename {DynamicData => src/DynamicData}/List/ChangeType.cs (100%) rename {DynamicData => src/DynamicData}/List/IChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/List/IChangeSetAdaptor.cs (100%) rename {DynamicData => src/DynamicData}/List/IExtendedList.cs (100%) rename {DynamicData => src/DynamicData}/List/IGroup.cs (100%) rename {DynamicData => src/DynamicData}/List/IGrouping.cs (100%) rename {DynamicData => src/DynamicData}/List/IObservableList.cs (100%) rename {DynamicData => src/DynamicData}/List/IPageChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/List/ISourceList.cs (100%) rename {DynamicData => src/DynamicData}/List/IVirtualChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/AnonymousObservableList.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/AutoRefresh.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/BufferIf.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Combiner.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/DeferUntilLoaded.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Distinct.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/DynamicCombiner.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/EditDiff.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/ExpirableItem.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/ExpireAfter.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Filter.cs (97%) rename {DynamicData => src/DynamicData}/List/Internal/FilterOnObservable.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/FilterOnProperty.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/FilterStatic.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Group.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/GroupOn.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/GroupOnImmutable.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/GroupOnProperty.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/GroupOnPropertyWithImmutableState.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/ImmutableGroup.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/MergeMany.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/OnBeingAdded.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/OnBeingRemoved.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Pager.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/QueryWhenChanged.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/ReaderWriter.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/RefCount.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/ReferenceCountTracker.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/SizeLimiter.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Sort.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/SubscribeMany.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Switch.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/ToObservableChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/TransformAsync.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/TransformMany.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Transformer.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/UnifiedChange.cs (100%) rename {DynamicData => src/DynamicData}/List/Internal/Virtualiser.cs (100%) rename {DynamicData => src/DynamicData}/List/ItemChange.cs (97%) rename {DynamicData => src/DynamicData}/List/Linq/AddKeyEnumerator.cs (100%) rename {DynamicData => src/DynamicData}/List/Linq/ItemChangeEnumerator.cs (100%) rename {DynamicData => src/DynamicData}/List/Linq/ReverseEnumerator.cs (100%) rename {DynamicData => src/DynamicData}/List/Linq/UnifiedChangeEnumerator.cs (100%) rename {DynamicData => src/DynamicData}/List/Linq/WithoutIndexEnumerator.cs (100%) rename {DynamicData => src/DynamicData}/List/ListChangeReason.cs (100%) rename {DynamicData => src/DynamicData}/List/ListEx.cs (100%) rename {DynamicData => src/DynamicData}/List/ListFilterPolicy.cs (100%) rename {DynamicData => src/DynamicData}/List/ObservableListEx.cs (98%) rename {DynamicData => src/DynamicData}/List/PageChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/List/RangeChange.cs (100%) rename {DynamicData => src/DynamicData}/List/SortException.cs (100%) rename {DynamicData => src/DynamicData}/List/SortOptions.cs (100%) rename {DynamicData => src/DynamicData}/List/SourceList.cs (96%) rename {DynamicData => src/DynamicData}/List/SourceListEditConvenienceEx.cs (100%) rename {DynamicData => src/DynamicData}/List/SourceListEx.cs (100%) rename {DynamicData => src/DynamicData}/List/Tests/ChangeSetAggregator.cs (100%) rename {DynamicData => src/DynamicData}/List/Tests/TestEx.cs (100%) rename {DynamicData => src/DynamicData}/List/UnspecifiedIndexException.cs (100%) rename {DynamicData => src/DynamicData}/List/VirtualChangeSet.cs (100%) rename {DynamicData => src/DynamicData}/ObservableChangeSet.cs (98%) rename {DynamicData => src/DynamicData}/ObsoleteEx.cs (100%) rename {DynamicData => src/DynamicData}/Platforms/net45/PLinqFilteredUpdater.cs (100%) rename {DynamicData => src/DynamicData}/Platforms/net45/PSubscribeMany.cs (100%) rename {DynamicData => src/DynamicData}/Platforms/net45/PTransform.cs (100%) rename {DynamicData => src/DynamicData}/Platforms/net45/ParallelEx.cs (100%) rename {DynamicData => src/DynamicData}/Platforms/net45/ParallelOperators.cs (100%) rename {DynamicData => src/DynamicData}/Platforms/net45/ParallelType.cs (100%) rename {DynamicData => src/DynamicData}/Platforms/net45/ParallelisationOptions.cs (100%) rename {DynamicData => src/DynamicData}/Properties/Annotations.cs (100%) rename global.json => src/global.json (100%) diff --git a/appveyor.yml b/appveyor.yml index bb929f432..22025e708 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,7 @@ build_script: - cmd: | dotnet --info - msbuild /restore /p:Configuration=Release /p:version=%APPVEYOR_BUILD_VERSION% -verbosity:minimal DynamicData.sln + msbuild /restore /p:Configuration=Release /p:version=%APPVEYOR_BUILD_VERSION% -verbosity:minimal src/DynamicData.sln appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\DynamicData\bin\Release\DynamicData.%APPVEYOR_BUILD_VERSION%.nupkg appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\DynamicData.ReactiveUI\bin\Release\DynamicData.ReactiveUI.%APPVEYOR_BUILD_VERSION%.nupkg @@ -28,8 +28,8 @@ test_script: - cmd: | cd %APPVEYOR_BUILD_FOLDER% - dotnet test DynamicData.Tests\DynamicData.Tests.csproj --configuration Release --no-build --no-restore - dotnet test DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj --configuration Release --no-build --no-restore + dotnet test src/DynamicData.Tests\DynamicData.Tests.csproj --configuration Release --no-build --no-restore + dotnet test src/DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj --configuration Release --no-build --no-restore deploy: - provider: NuGet @@ -62,9 +62,9 @@ before_build: - cmd: | appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe - nuget restore DynamicData.sln + nuget restore src/DynamicData.sln build: - project: DynamicData.sln + project: src/DynamicData.sln publish_nuget: true publish_nuget_symbols: true include_nuget_references: true @@ -113,14 +113,14 @@ build_script: - cmd: | dotnet --info - msbuild /restore /p:Configuration=Release /p:version=%APPVEYOR_BUILD_VERSION% -verbosity:minimal DynamicData.sln - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\DynamicData\bin\Release\DynamicData.%APPVEYOR_BUILD_VERSION%.nupkg - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\DynamicData.ReactiveUI\bin\Release\DynamicData.ReactiveUI.%APPVEYOR_BUILD_VERSION%.nupkg + msbuild /restore /p:Configuration=Release /p:version=%APPVEYOR_BUILD_VERSION% -verbosity:minimal src/DynamicData.sln + appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\src\DynamicData\bin\Release\DynamicData.%APPVEYOR_BUILD_VERSION%.nupkg + appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\src\DynamicData.ReactiveUI\bin\Release\DynamicData.ReactiveUI.%APPVEYOR_BUILD_VERSION%.nupkg test: off # Hangs indefinitely and I do not know why as the same command works locally test_script: - cmd: | cd %APPVEYOR_BUILD_FOLDER% - dotnet test DynamicData.Tests\DynamicData.Tests.csproj --configuration Release --no-build --no-restore - dotnet test DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj --configuration Release --no-build --no-restore + dotnet test src\DynamicData.Tests\DynamicData.Tests.csproj --configuration Release --no-build --no-restore + dotnet test src\DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj --configuration Release --no-build --no-restore diff --git a/Directory.Build.props b/src/Directory.Build.props similarity index 100% rename from Directory.Build.props rename to src/Directory.Build.props diff --git a/Directory.build.targets b/src/Directory.build.targets similarity index 100% rename from Directory.build.targets rename to src/Directory.build.targets diff --git a/DynamicData.Benchmarks/Cache/SourceCache.cs b/src/DynamicData.Benchmarks/Cache/SourceCache.cs similarity index 100% rename from DynamicData.Benchmarks/Cache/SourceCache.cs rename to src/DynamicData.Benchmarks/Cache/SourceCache.cs diff --git a/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj b/src/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj similarity index 100% rename from DynamicData.Benchmarks/DynamicData.Benchmarks.csproj rename to src/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj diff --git a/DynamicData.Benchmarks/List/GroupAdd.cs b/src/DynamicData.Benchmarks/List/GroupAdd.cs similarity index 100% rename from DynamicData.Benchmarks/List/GroupAdd.cs rename to src/DynamicData.Benchmarks/List/GroupAdd.cs diff --git a/DynamicData.Benchmarks/List/GroupRemove.cs b/src/DynamicData.Benchmarks/List/GroupRemove.cs similarity index 100% rename from DynamicData.Benchmarks/List/GroupRemove.cs rename to src/DynamicData.Benchmarks/List/GroupRemove.cs diff --git a/DynamicData.Benchmarks/List/SourceList.cs b/src/DynamicData.Benchmarks/List/SourceList.cs similarity index 100% rename from DynamicData.Benchmarks/List/SourceList.cs rename to src/DynamicData.Benchmarks/List/SourceList.cs diff --git a/DynamicData.Benchmarks/Program.cs b/src/DynamicData.Benchmarks/Program.cs similarity index 100% rename from DynamicData.Benchmarks/Program.cs rename to src/DynamicData.Benchmarks/Program.cs diff --git a/DynamicData.Profile/Allocations.cs b/src/DynamicData.Profile/Allocations.cs similarity index 100% rename from DynamicData.Profile/Allocations.cs rename to src/DynamicData.Profile/Allocations.cs diff --git a/DynamicData.Profile/AllocationsCount.cs b/src/DynamicData.Profile/AllocationsCount.cs similarity index 100% rename from DynamicData.Profile/AllocationsCount.cs rename to src/DynamicData.Profile/AllocationsCount.cs diff --git a/DynamicData.Profile/CacheAllocationChecks.cs b/src/DynamicData.Profile/CacheAllocationChecks.cs similarity index 100% rename from DynamicData.Profile/CacheAllocationChecks.cs rename to src/DynamicData.Profile/CacheAllocationChecks.cs diff --git a/DynamicData.Profile/CheapAndDirtyAllocationTest.cs b/src/DynamicData.Profile/CheapAndDirtyAllocationTest.cs similarity index 100% rename from DynamicData.Profile/CheapAndDirtyAllocationTest.cs rename to src/DynamicData.Profile/CheapAndDirtyAllocationTest.cs diff --git a/DynamicData.Profile/DynamicData.Profile.csproj b/src/DynamicData.Profile/DynamicData.Profile.csproj similarity index 100% rename from DynamicData.Profile/DynamicData.Profile.csproj rename to src/DynamicData.Profile/DynamicData.Profile.csproj diff --git a/DynamicData.Profile/Person.cs b/src/DynamicData.Profile/Person.cs similarity index 100% rename from DynamicData.Profile/Person.cs rename to src/DynamicData.Profile/Person.cs diff --git a/DynamicData.ReactiveUI.Tests/Domain/Person.cs b/src/DynamicData.ReactiveUI.Tests/Domain/Person.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Domain/Person.cs rename to src/DynamicData.ReactiveUI.Tests/Domain/Person.cs diff --git a/DynamicData.ReactiveUI.Tests/Domain/RandomPersonGenerator.cs b/src/DynamicData.ReactiveUI.Tests/Domain/RandomPersonGenerator.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Domain/RandomPersonGenerator.cs rename to src/DynamicData.ReactiveUI.Tests/Domain/RandomPersonGenerator.cs diff --git a/DynamicData.ReactiveUI.Tests/DynamicData.ReactiveUI.Tests.csproj b/src/DynamicData.ReactiveUI.Tests/DynamicData.ReactiveUI.Tests.csproj similarity index 100% rename from DynamicData.ReactiveUI.Tests/DynamicData.ReactiveUI.Tests.csproj rename to src/DynamicData.ReactiveUI.Tests/DynamicData.ReactiveUI.Tests.csproj diff --git a/DynamicData.ReactiveUI.Tests/Fixtures/BindChangeSetFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/BindChangeSetFixture.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Fixtures/BindChangeSetFixture.cs rename to src/DynamicData.ReactiveUI.Tests/Fixtures/BindChangeSetFixture.cs diff --git a/DynamicData.ReactiveUI.Tests/Fixtures/BindFromObservableListFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/BindFromObservableListFixture.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Fixtures/BindFromObservableListFixture.cs rename to src/DynamicData.ReactiveUI.Tests/Fixtures/BindFromObservableListFixture.cs diff --git a/DynamicData.ReactiveUI.Tests/Fixtures/BindSortedChangeSetFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/BindSortedChangeSetFixture.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Fixtures/BindSortedChangeSetFixture.cs rename to src/DynamicData.ReactiveUI.Tests/Fixtures/BindSortedChangeSetFixture.cs diff --git a/DynamicData.ReactiveUI.Tests/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs rename to src/DynamicData.ReactiveUI.Tests/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs diff --git a/DynamicData.ReactiveUI.Tests/Fixtures/ToObservableChangeSetFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/ToObservableChangeSetFixture.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Fixtures/ToObservableChangeSetFixture.cs rename to src/DynamicData.ReactiveUI.Tests/Fixtures/ToObservableChangeSetFixture.cs diff --git a/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyFixture.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Fixtures/TransformManyFixture.cs rename to src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyFixture.cs diff --git a/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionFixture.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionFixture.cs rename to src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionFixture.cs diff --git a/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs similarity index 100% rename from DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs rename to src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs diff --git a/DynamicData.ReactiveUI/DynamicData.ReactiveUI.csproj b/src/DynamicData.ReactiveUI/DynamicData.ReactiveUI.csproj similarity index 100% rename from DynamicData.ReactiveUI/DynamicData.ReactiveUI.csproj rename to src/DynamicData.ReactiveUI/DynamicData.ReactiveUI.csproj diff --git a/DynamicData.ReactiveUI/DynamicDataEx.cs b/src/DynamicData.ReactiveUI/DynamicDataEx.cs similarity index 100% rename from DynamicData.ReactiveUI/DynamicDataEx.cs rename to src/DynamicData.ReactiveUI/DynamicDataEx.cs diff --git a/DynamicData.ReactiveUI/ObservableCacheToReactiveListAdaptor.cs b/src/DynamicData.ReactiveUI/ObservableCacheToReactiveListAdaptor.cs similarity index 100% rename from DynamicData.ReactiveUI/ObservableCacheToReactiveListAdaptor.cs rename to src/DynamicData.ReactiveUI/ObservableCacheToReactiveListAdaptor.cs diff --git a/DynamicData.ReactiveUI/ObservableListToReactiveListAdaptor.cs b/src/DynamicData.ReactiveUI/ObservableListToReactiveListAdaptor.cs similarity index 100% rename from DynamicData.ReactiveUI/ObservableListToReactiveListAdaptor.cs rename to src/DynamicData.ReactiveUI/ObservableListToReactiveListAdaptor.cs diff --git a/DynamicData.ReactiveUI/ReactiveListEx.cs b/src/DynamicData.ReactiveUI/ReactiveListEx.cs similarity index 100% rename from DynamicData.ReactiveUI/ReactiveListEx.cs rename to src/DynamicData.ReactiveUI/ReactiveListEx.cs diff --git a/DynamicData.ReactiveUI/SortedReactiveListAdaptor.cs b/src/DynamicData.ReactiveUI/SortedReactiveListAdaptor.cs similarity index 100% rename from DynamicData.ReactiveUI/SortedReactiveListAdaptor.cs rename to src/DynamicData.ReactiveUI/SortedReactiveListAdaptor.cs diff --git a/DynamicData.Tests/AggregationTests/AggregationFixture.cs b/src/DynamicData.Tests/AggregationTests/AggregationFixture.cs similarity index 100% rename from DynamicData.Tests/AggregationTests/AggregationFixture.cs rename to src/DynamicData.Tests/AggregationTests/AggregationFixture.cs diff --git a/DynamicData.Tests/AggregationTests/AverageFixture.cs b/src/DynamicData.Tests/AggregationTests/AverageFixture.cs similarity index 100% rename from DynamicData.Tests/AggregationTests/AverageFixture.cs rename to src/DynamicData.Tests/AggregationTests/AverageFixture.cs diff --git a/DynamicData.Tests/AggregationTests/MaxFixture.cs b/src/DynamicData.Tests/AggregationTests/MaxFixture.cs similarity index 100% rename from DynamicData.Tests/AggregationTests/MaxFixture.cs rename to src/DynamicData.Tests/AggregationTests/MaxFixture.cs diff --git a/DynamicData.Tests/AggregationTests/MinFixture.cs b/src/DynamicData.Tests/AggregationTests/MinFixture.cs similarity index 100% rename from DynamicData.Tests/AggregationTests/MinFixture.cs rename to src/DynamicData.Tests/AggregationTests/MinFixture.cs diff --git a/DynamicData.Tests/AggregationTests/SumFixture.cs b/src/DynamicData.Tests/AggregationTests/SumFixture.cs similarity index 100% rename from DynamicData.Tests/AggregationTests/SumFixture.cs rename to src/DynamicData.Tests/AggregationTests/SumFixture.cs diff --git a/DynamicData.Tests/AutoRefreshFilter.cs b/src/DynamicData.Tests/AutoRefreshFilter.cs similarity index 100% rename from DynamicData.Tests/AutoRefreshFilter.cs rename to src/DynamicData.Tests/AutoRefreshFilter.cs diff --git a/DynamicData.Tests/Binding/BindingLIstBindListFixture.cs b/src/DynamicData.Tests/Binding/BindingLIstBindListFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/BindingLIstBindListFixture.cs rename to src/DynamicData.Tests/Binding/BindingLIstBindListFixture.cs diff --git a/DynamicData.Tests/Binding/BindingListBindCacheFixture.cs b/src/DynamicData.Tests/Binding/BindingListBindCacheFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/BindingListBindCacheFixture.cs rename to src/DynamicData.Tests/Binding/BindingListBindCacheFixture.cs diff --git a/DynamicData.Tests/Binding/BindingListBindCacheSortedFixture.cs b/src/DynamicData.Tests/Binding/BindingListBindCacheSortedFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/BindingListBindCacheSortedFixture.cs rename to src/DynamicData.Tests/Binding/BindingListBindCacheSortedFixture.cs diff --git a/DynamicData.Tests/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs b/src/DynamicData.Tests/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs rename to src/DynamicData.Tests/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs diff --git a/DynamicData.Tests/Binding/NotifyPropertyChangedExFixture.cs b/src/DynamicData.Tests/Binding/NotifyPropertyChangedExFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/NotifyPropertyChangedExFixture.cs rename to src/DynamicData.Tests/Binding/NotifyPropertyChangedExFixture.cs diff --git a/DynamicData.Tests/Binding/ObservableCollectionBindCacheFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/ObservableCollectionBindCacheFixture.cs rename to src/DynamicData.Tests/Binding/ObservableCollectionBindCacheFixture.cs diff --git a/DynamicData.Tests/Binding/ObservableCollectionBindCacheSortedFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheSortedFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/ObservableCollectionBindCacheSortedFixture.cs rename to src/DynamicData.Tests/Binding/ObservableCollectionBindCacheSortedFixture.cs diff --git a/DynamicData.Tests/Binding/ObservableCollectionBindListFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionBindListFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/ObservableCollectionBindListFixture.cs rename to src/DynamicData.Tests/Binding/ObservableCollectionBindListFixture.cs diff --git a/DynamicData.Tests/Binding/ObservableCollectionExtendedToChangeSetFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionExtendedToChangeSetFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/ObservableCollectionExtendedToChangeSetFixture.cs rename to src/DynamicData.Tests/Binding/ObservableCollectionExtendedToChangeSetFixture.cs diff --git a/DynamicData.Tests/Binding/ObservableCollectionToChangeSetFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionToChangeSetFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/ObservableCollectionToChangeSetFixture.cs rename to src/DynamicData.Tests/Binding/ObservableCollectionToChangeSetFixture.cs diff --git a/DynamicData.Tests/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs b/src/DynamicData.Tests/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs similarity index 100% rename from DynamicData.Tests/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs rename to src/DynamicData.Tests/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs diff --git a/DynamicData.Tests/Cache/AndFixture.cs b/src/DynamicData.Tests/Cache/AndFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/AndFixture.cs rename to src/DynamicData.Tests/Cache/AndFixture.cs diff --git a/DynamicData.Tests/Cache/AutoRefreshFixture.cs b/src/DynamicData.Tests/Cache/AutoRefreshFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/AutoRefreshFixture.cs rename to src/DynamicData.Tests/Cache/AutoRefreshFixture.cs diff --git a/DynamicData.Tests/Cache/BatchFixture.cs b/src/DynamicData.Tests/Cache/BatchFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/BatchFixture.cs rename to src/DynamicData.Tests/Cache/BatchFixture.cs diff --git a/DynamicData.Tests/Cache/BatchIfFixture.cs b/src/DynamicData.Tests/Cache/BatchIfFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/BatchIfFixture.cs rename to src/DynamicData.Tests/Cache/BatchIfFixture.cs diff --git a/DynamicData.Tests/Cache/BatchIfWithTimeOutFixture.cs b/src/DynamicData.Tests/Cache/BatchIfWithTimeOutFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/BatchIfWithTimeOutFixture.cs rename to src/DynamicData.Tests/Cache/BatchIfWithTimeOutFixture.cs diff --git a/DynamicData.Tests/Cache/BufferInitialFixture.cs b/src/DynamicData.Tests/Cache/BufferInitialFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/BufferInitialFixture.cs rename to src/DynamicData.Tests/Cache/BufferInitialFixture.cs diff --git a/DynamicData.Tests/Cache/ChangesReducerFixture.cs b/src/DynamicData.Tests/Cache/ChangesReducerFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ChangesReducerFixture.cs rename to src/DynamicData.Tests/Cache/ChangesReducerFixture.cs diff --git a/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs b/src/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs similarity index 96% rename from DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs rename to src/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs index 4264b76fb..756f6f925 100644 --- a/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs +++ b/src/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs @@ -1,58 +1,58 @@ -using System; -using System.Linq; -using DynamicData.Tests.Domain; -using FluentAssertions; -using Xunit; - -namespace DynamicData.Tests.Cache -{ - - public class DeferAnsdSkipFixture - { - [Fact] - public void DeferUntilLoadedDoesNothingUntilDataHasBeenReceived() - { - bool updateReceived = false; - IChangeSet result = null; - - var cache = new SourceCache(p => p.Name); - - var deferStream = cache.Connect().DeferUntilLoaded() - .Subscribe(changes => - { - updateReceived = true; - result = changes; - }); - - var person = new Person("Test", 1); - updateReceived.Should().BeFalse(); - cache.AddOrUpdate(person); - - updateReceived.Should().BeTrue(); - result.Adds.Should().Be(1); - result.First().Current.Should().Be(person); - deferStream.Dispose(); - } - - [Fact] - public void SkipInitialDoesNotReturnTheFirstBatchOfData() - { - bool updateReceived = false; - - var cache = new SourceCache(p => p.Name); - - var deferStream = cache.Connect().SkipInitial() - .Subscribe(changes => updateReceived = true); - - updateReceived.Should().BeFalse(); - - cache.AddOrUpdate(new Person("P1", 1)); - - updateReceived.Should().BeFalse(); - - cache.AddOrUpdate(new Person("P2", 2)); - updateReceived.Should().BeTrue(); - deferStream.Dispose(); - } - } -} +using System; +using System.Linq; +using DynamicData.Tests.Domain; +using FluentAssertions; +using Xunit; + +namespace DynamicData.Tests.Cache +{ + + public class DeferAnsdSkipFixture + { + [Fact] + public void DeferUntilLoadedDoesNothingUntilDataHasBeenReceived() + { + bool updateReceived = false; + IChangeSet result = null; + + var cache = new SourceCache(p => p.Name); + + var deferStream = cache.Connect().DeferUntilLoaded() + .Subscribe(changes => + { + updateReceived = true; + result = changes; + }); + + var person = new Person("Test", 1); + updateReceived.Should().BeFalse(); + cache.AddOrUpdate(person); + + updateReceived.Should().BeTrue(); + result.Adds.Should().Be(1); + result.First().Current.Should().Be(person); + deferStream.Dispose(); + } + + [Fact] + public void SkipInitialDoesNotReturnTheFirstBatchOfData() + { + bool updateReceived = false; + + var cache = new SourceCache(p => p.Name); + + var deferStream = cache.Connect().SkipInitial() + .Subscribe(changes => updateReceived = true); + + updateReceived.Should().BeFalse(); + + cache.AddOrUpdate(new Person("P1", 1)); + + updateReceived.Should().BeFalse(); + + cache.AddOrUpdate(new Person("P2", 2)); + updateReceived.Should().BeTrue(); + deferStream.Dispose(); + } + } +} diff --git a/DynamicData.Tests/Cache/DisposeManyFixture.cs b/src/DynamicData.Tests/Cache/DisposeManyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/DisposeManyFixture.cs rename to src/DynamicData.Tests/Cache/DisposeManyFixture.cs diff --git a/DynamicData.Tests/Cache/DistinctFixture.cs b/src/DynamicData.Tests/Cache/DistinctFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/DistinctFixture.cs rename to src/DynamicData.Tests/Cache/DistinctFixture.cs diff --git a/DynamicData.Tests/Cache/DynamicAndFixture.cs b/src/DynamicData.Tests/Cache/DynamicAndFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/DynamicAndFixture.cs rename to src/DynamicData.Tests/Cache/DynamicAndFixture.cs diff --git a/DynamicData.Tests/Cache/DynamicExceptFixture.cs b/src/DynamicData.Tests/Cache/DynamicExceptFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/DynamicExceptFixture.cs rename to src/DynamicData.Tests/Cache/DynamicExceptFixture.cs diff --git a/DynamicData.Tests/Cache/DynamicOrFixture.cs b/src/DynamicData.Tests/Cache/DynamicOrFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/DynamicOrFixture.cs rename to src/DynamicData.Tests/Cache/DynamicOrFixture.cs diff --git a/DynamicData.Tests/Cache/DynamicXorFixture.cs b/src/DynamicData.Tests/Cache/DynamicXorFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/DynamicXorFixture.cs rename to src/DynamicData.Tests/Cache/DynamicXorFixture.cs diff --git a/DynamicData.Tests/Cache/EditDiffFixture.cs b/src/DynamicData.Tests/Cache/EditDiffFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/EditDiffFixture.cs rename to src/DynamicData.Tests/Cache/EditDiffFixture.cs diff --git a/DynamicData.Tests/Cache/EnumerableObservableToObservableChangeSetFixture.cs b/src/DynamicData.Tests/Cache/EnumerableObservableToObservableChangeSetFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/EnumerableObservableToObservableChangeSetFixture.cs rename to src/DynamicData.Tests/Cache/EnumerableObservableToObservableChangeSetFixture.cs diff --git a/DynamicData.Tests/Cache/ExceptFixture.cs b/src/DynamicData.Tests/Cache/ExceptFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ExceptFixture.cs rename to src/DynamicData.Tests/Cache/ExceptFixture.cs diff --git a/DynamicData.Tests/Cache/ExpireAfterFixture.cs b/src/DynamicData.Tests/Cache/ExpireAfterFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ExpireAfterFixture.cs rename to src/DynamicData.Tests/Cache/ExpireAfterFixture.cs diff --git a/DynamicData.Tests/Cache/FilterControllerFixture.cs b/src/DynamicData.Tests/Cache/FilterControllerFixture.cs similarity index 97% rename from DynamicData.Tests/Cache/FilterControllerFixture.cs rename to src/DynamicData.Tests/Cache/FilterControllerFixture.cs index 54a7cab5f..7ce112eac 100644 --- a/DynamicData.Tests/Cache/FilterControllerFixture.cs +++ b/src/DynamicData.Tests/Cache/FilterControllerFixture.cs @@ -1,303 +1,303 @@ -using System; -using System.Linq; -using System.Reactive.Subjects; -using DynamicData.Tests.Domain; -using FluentAssertions; -using Xunit; -using System.Reactive; -using System.Reactive.Linq; - -namespace DynamicData.Tests.Cache -{ - - public class FilterControllerFixture: IDisposable - { - private readonly ISourceCache _source; - private readonly ChangeSetAggregator _results; - private readonly ISubject> _filter; - - - public FilterControllerFixture() - { - _source = new SourceCache(p => p.Key); - _filter = new BehaviorSubject>(p => p.Age > 20); - _results = new ChangeSetAggregator(_source.Connect().Filter(_filter)); - } - - public void Dispose() - { - _source.Dispose(); - _results.Dispose(); - } - - [Fact] - public void ChangeFilter() - { - var people = Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray(); - - _source.AddOrUpdate(people); - _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); - - _filter.OnNext(p => p.Age <= 50); - _results.Data.Count.Should().Be(50, "Should be 50 people in the cache"); - _results.Messages.Count.Should().Be(2, "Should be 2 update messages"); - _results.Messages[1].Removes.Should().Be(50, "Should be 50 removes in the second message"); - _results.Messages[1].Adds.Should().Be(20, "Should be 20 adds in the second message"); - - _results.Data.Items.All(p => p.Age <= 50).Should().BeTrue(); - } - - [Fact] - public void ReapplyFilterDoesntThrow() - { - using (var source = new SourceCache(p => p.Key)) - { - source.AddOrUpdate(Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray()); - - var ex = Record.Exception(() => source.Connect() - .Filter(Observable.Return(Unit.Default)) - .AsObservableCache()); - Assert.Null(ex); - } - } - - [Fact] - public void RepeatedApply() - { - using (var source = new SourceCache(p => p.Key)) - { - source.AddOrUpdate(Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray()); - _filter.OnNext(p => true); - - IChangeSet latestChanges = null; - using (source.Connect().Filter(_filter) - .Do(changes => latestChanges = changes).AsObservableCache()) - { - _filter.OnNext(p => false); - latestChanges.Removes.Should().Be(100); - latestChanges.Adds.Should().Be(0); - - _filter.OnNext(p => true); - latestChanges.Adds.Should().Be(100); - latestChanges.Removes.Should().Be(0); - - _filter.OnNext(p => false); - latestChanges.Removes.Should().Be(100); - latestChanges.Adds.Should().Be(0); - - _filter.OnNext(p => true); - latestChanges.Adds.Should().Be(100); - latestChanges.Removes.Should().Be(0); - - _filter.OnNext(p => false); - latestChanges.Removes.Should().Be(100); - latestChanges.Adds.Should().Be(0); - } - } - } - - [Fact] - public void ReevaluateFilter() - { - //re-evaluate for inline changes - var people = Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray(); - - _source.AddOrUpdate(people); - _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); - - foreach (var person in people) - { - person.Age = person.Age + 10; - } - _filter.OnNext(p => p.Age > 20); - - _results.Data.Count.Should().Be(90, "Should be 90 people in the cache"); - _results.Messages.Count.Should().Be(2, "Should be 2 update messages"); - _results.Messages[1].Adds.Should().Be(10, "Should be 10 adds in the second message"); - - foreach (var person in people) - { - person.Age = person.Age - 10; - } - _filter.OnNext(p => p.Age > 20); - - _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); - _results.Messages.Count.Should().Be(3, "Should be 3 update messages"); - _results.Messages[2].Removes.Should().Be(10, "Should be 10 removes in the third message"); - } - - - #region Static filter tests - - /* Should be the same as standard lambda filter */ - - [Fact] - public void AddMatched() - { - var person = new Person("Adult1", 50); - _source.AddOrUpdate(person); - - _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - _results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); - _results.Data.Items.First().Should().Be(person, "Should be same person"); - } - - [Fact] - public void AddNotMatched() - { - var person = new Person("Adult1", 10); - _source.AddOrUpdate(person); - - _results.Messages.Count.Should().Be(0, "Should have no item updates"); - _results.Data.Count.Should().Be(0, "Cache should have no items"); - } - - [Fact] - public void AddNotMatchedAndUpdateMatched() - { - const string key = "Adult1"; - var notmatched = new Person(key, 19); - var matched = new Person(key, 21); - - _source.Edit(innerCache => - { - innerCache.AddOrUpdate(notmatched); - innerCache.AddOrUpdate(matched); - }); - - _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - _results.Messages[0].First().Current.Should().Be(matched, "Should be same person"); - _results.Data.Items.First().Should().Be(matched, "Should be same person"); - } - - [Fact] - public void AttemptedRemovalOfANonExistentKeyWillBeIgnored() - { - const string key = "Adult1"; - _source.Remove(key); - _results.Messages.Count.Should().Be(0, "Should be 0 updates"); - } - - [Fact] - public void BatchOfUniqueUpdates() - { - var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - _source.AddOrUpdate(people); - _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - _results.Messages[0].Adds.Should().Be(80, "Should return 80 adds"); - - var filtered = people.Where(p => p.Age > 20).OrderBy(p => p.Age).ToArray(); - _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect Filter result"); - } - - [Fact] - public void BatchRemoves() - { - var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - - _source.AddOrUpdate(people); - _source.Remove(people); - - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Adds.Should().Be(80, "Should be 80 addes"); - _results.Messages[1].Removes.Should().Be(80, "Should be 80 removes"); - _results.Data.Count.Should().Be(0, "Should be nothing cached"); - } - - [Fact] - public void BatchSuccessiveUpdates() - { - var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - foreach (var person in people) - { - Person person1 = person; - _source.AddOrUpdate(person1); - } - - _results.Messages.Count.Should().Be(80, "Should be 80 messages"); - _results.Data.Count.Should().Be(80, "Should be 80 in the cache"); - var filtered = people.Where(p => p.Age > 20).OrderBy(p => p.Age).ToArray(); - _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect Filter result"); - } - - [Fact] - public void Clear() - { - var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - _source.AddOrUpdate(people); - _source.Clear(); - - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Adds.Should().Be(80, "Should be 80 addes"); - _results.Messages[1].Removes.Should().Be(80, "Should be 80 removes"); - _results.Data.Count.Should().Be(0, "Should be nothing cached"); - } - - [Fact] - public void Remove() - { - const string key = "Adult1"; - var person = new Person(key, 50); - - _source.AddOrUpdate(person); - _source.Remove(key); - - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Adds.Should().Be(1, "Should be 80 addes"); - _results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); - _results.Data.Count.Should().Be(0, "Should be nothing cached"); - } - - [Fact] - public void UpdateMatched() - { - const string key = "Adult1"; - var newperson = new Person(key, 50); - var updated = new Person(key, 51); - - _source.AddOrUpdate(newperson); - _source.AddOrUpdate(updated); - - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); - _results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); - } - - [Fact] - public void SameKeyChanges() - { - const string key = "Adult1"; - - _source.Edit(updater => - { - updater.AddOrUpdate(new Person(key, 50)); - updater.AddOrUpdate(new Person(key, 52)); - updater.AddOrUpdate(new Person(key, 53)); - updater.Remove(key); - }); - - _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); - _results.Messages[0].Updates.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Removes.Should().Be(1, "Should be 1 remove"); - } - - [Fact] - public void UpdateNotMatched() - { - const string key = "Adult1"; - var newperson = new Person(key, 10); - var updated = new Person(key, 11); - - _source.AddOrUpdate(newperson); - _source.AddOrUpdate(updated); - - _results.Messages.Count.Should().Be(0, "Should be no updates"); - _results.Data.Count.Should().Be(0, "Should nothing cached"); - } - - #endregion - } -} +using System; +using System.Linq; +using System.Reactive.Subjects; +using DynamicData.Tests.Domain; +using FluentAssertions; +using Xunit; +using System.Reactive; +using System.Reactive.Linq; + +namespace DynamicData.Tests.Cache +{ + + public class FilterControllerFixture: IDisposable + { + private readonly ISourceCache _source; + private readonly ChangeSetAggregator _results; + private readonly ISubject> _filter; + + + public FilterControllerFixture() + { + _source = new SourceCache(p => p.Key); + _filter = new BehaviorSubject>(p => p.Age > 20); + _results = new ChangeSetAggregator(_source.Connect().Filter(_filter)); + } + + public void Dispose() + { + _source.Dispose(); + _results.Dispose(); + } + + [Fact] + public void ChangeFilter() + { + var people = Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray(); + + _source.AddOrUpdate(people); + _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); + + _filter.OnNext(p => p.Age <= 50); + _results.Data.Count.Should().Be(50, "Should be 50 people in the cache"); + _results.Messages.Count.Should().Be(2, "Should be 2 update messages"); + _results.Messages[1].Removes.Should().Be(50, "Should be 50 removes in the second message"); + _results.Messages[1].Adds.Should().Be(20, "Should be 20 adds in the second message"); + + _results.Data.Items.All(p => p.Age <= 50).Should().BeTrue(); + } + + [Fact] + public void ReapplyFilterDoesntThrow() + { + using (var source = new SourceCache(p => p.Key)) + { + source.AddOrUpdate(Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray()); + + var ex = Record.Exception(() => source.Connect() + .Filter(Observable.Return(Unit.Default)) + .AsObservableCache()); + Assert.Null(ex); + } + } + + [Fact] + public void RepeatedApply() + { + using (var source = new SourceCache(p => p.Key)) + { + source.AddOrUpdate(Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray()); + _filter.OnNext(p => true); + + IChangeSet latestChanges = null; + using (source.Connect().Filter(_filter) + .Do(changes => latestChanges = changes).AsObservableCache()) + { + _filter.OnNext(p => false); + latestChanges.Removes.Should().Be(100); + latestChanges.Adds.Should().Be(0); + + _filter.OnNext(p => true); + latestChanges.Adds.Should().Be(100); + latestChanges.Removes.Should().Be(0); + + _filter.OnNext(p => false); + latestChanges.Removes.Should().Be(100); + latestChanges.Adds.Should().Be(0); + + _filter.OnNext(p => true); + latestChanges.Adds.Should().Be(100); + latestChanges.Removes.Should().Be(0); + + _filter.OnNext(p => false); + latestChanges.Removes.Should().Be(100); + latestChanges.Adds.Should().Be(0); + } + } + } + + [Fact] + public void ReevaluateFilter() + { + //re-evaluate for inline changes + var people = Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray(); + + _source.AddOrUpdate(people); + _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); + + foreach (var person in people) + { + person.Age = person.Age + 10; + } + _filter.OnNext(p => p.Age > 20); + + _results.Data.Count.Should().Be(90, "Should be 90 people in the cache"); + _results.Messages.Count.Should().Be(2, "Should be 2 update messages"); + _results.Messages[1].Adds.Should().Be(10, "Should be 10 adds in the second message"); + + foreach (var person in people) + { + person.Age = person.Age - 10; + } + _filter.OnNext(p => p.Age > 20); + + _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); + _results.Messages.Count.Should().Be(3, "Should be 3 update messages"); + _results.Messages[2].Removes.Should().Be(10, "Should be 10 removes in the third message"); + } + + + #region Static filter tests + + /* Should be the same as standard lambda filter */ + + [Fact] + public void AddMatched() + { + var person = new Person("Adult1", 50); + _source.AddOrUpdate(person); + + _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + _results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); + _results.Data.Items.First().Should().Be(person, "Should be same person"); + } + + [Fact] + public void AddNotMatched() + { + var person = new Person("Adult1", 10); + _source.AddOrUpdate(person); + + _results.Messages.Count.Should().Be(0, "Should have no item updates"); + _results.Data.Count.Should().Be(0, "Cache should have no items"); + } + + [Fact] + public void AddNotMatchedAndUpdateMatched() + { + const string key = "Adult1"; + var notmatched = new Person(key, 19); + var matched = new Person(key, 21); + + _source.Edit(innerCache => + { + innerCache.AddOrUpdate(notmatched); + innerCache.AddOrUpdate(matched); + }); + + _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + _results.Messages[0].First().Current.Should().Be(matched, "Should be same person"); + _results.Data.Items.First().Should().Be(matched, "Should be same person"); + } + + [Fact] + public void AttemptedRemovalOfANonExistentKeyWillBeIgnored() + { + const string key = "Adult1"; + _source.Remove(key); + _results.Messages.Count.Should().Be(0, "Should be 0 updates"); + } + + [Fact] + public void BatchOfUniqueUpdates() + { + var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); + + _source.AddOrUpdate(people); + _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + _results.Messages[0].Adds.Should().Be(80, "Should return 80 adds"); + + var filtered = people.Where(p => p.Age > 20).OrderBy(p => p.Age).ToArray(); + _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect Filter result"); + } + + [Fact] + public void BatchRemoves() + { + var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + + _source.AddOrUpdate(people); + _source.Remove(people); + + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Adds.Should().Be(80, "Should be 80 addes"); + _results.Messages[1].Removes.Should().Be(80, "Should be 80 removes"); + _results.Data.Count.Should().Be(0, "Should be nothing cached"); + } + + [Fact] + public void BatchSuccessiveUpdates() + { + var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + foreach (var person in people) + { + Person person1 = person; + _source.AddOrUpdate(person1); + } + + _results.Messages.Count.Should().Be(80, "Should be 80 messages"); + _results.Data.Count.Should().Be(80, "Should be 80 in the cache"); + var filtered = people.Where(p => p.Age > 20).OrderBy(p => p.Age).ToArray(); + _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect Filter result"); + } + + [Fact] + public void Clear() + { + var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + _source.AddOrUpdate(people); + _source.Clear(); + + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Adds.Should().Be(80, "Should be 80 addes"); + _results.Messages[1].Removes.Should().Be(80, "Should be 80 removes"); + _results.Data.Count.Should().Be(0, "Should be nothing cached"); + } + + [Fact] + public void Remove() + { + const string key = "Adult1"; + var person = new Person(key, 50); + + _source.AddOrUpdate(person); + _source.Remove(key); + + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Adds.Should().Be(1, "Should be 80 addes"); + _results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); + _results.Data.Count.Should().Be(0, "Should be nothing cached"); + } + + [Fact] + public void UpdateMatched() + { + const string key = "Adult1"; + var newperson = new Person(key, 50); + var updated = new Person(key, 51); + + _source.AddOrUpdate(newperson); + _source.AddOrUpdate(updated); + + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); + _results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); + } + + [Fact] + public void SameKeyChanges() + { + const string key = "Adult1"; + + _source.Edit(updater => + { + updater.AddOrUpdate(new Person(key, 50)); + updater.AddOrUpdate(new Person(key, 52)); + updater.AddOrUpdate(new Person(key, 53)); + updater.Remove(key); + }); + + _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); + _results.Messages[0].Updates.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Removes.Should().Be(1, "Should be 1 remove"); + } + + [Fact] + public void UpdateNotMatched() + { + const string key = "Adult1"; + var newperson = new Person(key, 10); + var updated = new Person(key, 11); + + _source.AddOrUpdate(newperson); + _source.AddOrUpdate(updated); + + _results.Messages.Count.Should().Be(0, "Should be no updates"); + _results.Data.Count.Should().Be(0, "Should nothing cached"); + } + + #endregion + } +} diff --git a/DynamicData.Tests/Cache/FilterFixture.cs b/src/DynamicData.Tests/Cache/FilterFixture.cs similarity index 97% rename from DynamicData.Tests/Cache/FilterFixture.cs rename to src/DynamicData.Tests/Cache/FilterFixture.cs index 937b749b9..9ec484d30 100644 --- a/DynamicData.Tests/Cache/FilterFixture.cs +++ b/src/DynamicData.Tests/Cache/FilterFixture.cs @@ -1,214 +1,214 @@ -using System; -using System.Linq; -using System.Reactive.Linq; -using DynamicData.Tests.Domain; -using FluentAssertions; -using Xunit; - -namespace DynamicData.Tests.Cache -{ - - public class FilterFixture: IDisposable - { - private readonly ISourceCache _source; - private readonly ChangeSetAggregator _results; - - public FilterFixture() - { - _source = new SourceCache(p => p.Name); - _results = _source.Connect(p => p.Age > 20).AsAggregator(); - } - - public void Dispose() - { - _source.Dispose(); - _results.Dispose(); - } - - [Fact] - public void AddMatched() - { - var person = new Person("Adult1", 50); - _source.AddOrUpdate(person); - - _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - _results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); - _results.Data.Items.First().Should().Be(person, "Should be same person"); - } - - [Fact] - public void AddNotMatched() - { - var person = new Person("Adult1", 10); - _source.AddOrUpdate(person); - - _results.Messages.Count.Should().Be(0, "Should have no item updates"); - _results.Data.Count.Should().Be(0, "Cache should have no items"); - } - - [Fact] - public void AddNotMatchedAndUpdateMatched() - { - const string key = "Adult1"; - var notmatched = new Person(key, 19); - var matched = new Person(key, 21); - - _source.Edit(updater => - { - updater.AddOrUpdate(notmatched); - updater.AddOrUpdate(matched); - }); - - _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - _results.Messages[0].First().Current.Should().Be(matched, "Should be same person"); - _results.Data.Items.First().Should().Be(matched, "Should be same person"); - } - - [Fact] - public void AttemptedRemovalOfANonExistentKeyWillBeIgnored() - { - const string key = "Adult1"; - _source.Remove(key); - _results.Messages.Count.Should().Be(0, "Should be 0 updates"); - } - - [Fact] - public void BatchOfUniqueUpdates() - { - var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - _source.AddOrUpdate(people); - _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - _results.Messages[0].Adds.Should().Be(80, "Should return 80 adds"); - - var filtered = people.Where(p => p.Age > 20).OrderBy(p => p.Age).ToArray(); - _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect Filter result"); - } - - [Fact] - public void BatchRemoves() - { - var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - - _source.AddOrUpdate(people); - _source.Remove(people); - - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Adds.Should().Be(80, "Should be 80 addes"); - _results.Messages[1].Removes.Should().Be(80, "Should be 80 removes"); - _results.Data.Count.Should().Be(0, "Should be nothing cached"); - } - - [Fact] - public void BatchSuccessiveUpdates() - { - var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - foreach (var person in people) - { - Person person1 = person; - _source.AddOrUpdate(person1); - } - - _results.Messages.Count.Should().Be(80, "Should be 100 updates"); - _results.Data.Count.Should().Be(80, "Should be 100 in the cache"); - var filtered = people.Where(p => p.Age > 20).OrderBy(p => p.Age).ToArray(); - _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect Filter result"); - } - - [Fact] - public void Clear() - { - var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - _source.AddOrUpdate(people); - _source.Clear(); - - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Adds.Should().Be(80, "Should be 80 addes"); - _results.Messages[1].Removes.Should().Be(80, "Should be 80 removes"); - _results.Data.Count.Should().Be(0, "Should be nothing cached"); - } - - [Fact] - public void Remove() - { - const string key = "Adult1"; - var person = new Person(key, 50); - - _source.AddOrUpdate(person); - _source.Remove(person); - - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Adds.Should().Be(1, "Should be 80 adds"); - _results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); - _results.Data.Count.Should().Be(0, "Should be nothing cached"); - } - - [Fact] - public void UpdateMatched() - - { - const string key = "Adult1"; - var newperson = new Person(key, 50); - var updated = new Person(key, 51); - _source.AddOrUpdate(newperson); - _source.AddOrUpdate(updated); - - _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); - _results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); - } - - [Fact] - public void SameKeyChanges() - { - const string key = "Adult1"; - - _source.Edit(updater => - { - updater.AddOrUpdate(new Person(key, 50)); - updater.AddOrUpdate(new Person(key, 52)); - updater.AddOrUpdate(new Person(key, 53)); - updater.Remove(key); - }); - - _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); - _results.Messages[0].Updates.Should().Be(2, "Should be 2 updates"); - _results.Messages[0].Removes.Should().Be(1, "Should be 1 remove"); - } - - [Fact] - public void UpdateNotMatched() - { - const string key = "Adult1"; - var newperson = new Person(key, 10); - var updated = new Person(key, 11); - - _source.AddOrUpdate(newperson); - _source.AddOrUpdate(updated); - - _results.Messages.Count.Should().Be(0, "Should be no updates"); - _results.Data.Count.Should().Be(0, "Should nothing cached"); - } - - [Fact] - public void DuplicateKeyWithMerge() - { - const string key = "Adult1"; - var newperson = new Person(key, 30); - - using (var results = _source.Connect() - .Merge(_source.Connect()) - .Filter(p => p.Age > 20).AsAggregator()) - { - _source.AddOrUpdate(newperson); // previously this would throw an exception - - results.Messages.Count.Should().Be(2, "Should be 2 messages"); - results.Messages[0].Adds.Should().Be(1, "Should be 1 add"); - results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); - results.Data.Count.Should().Be(1, "Should be cached"); - } - } - } -} +using System; +using System.Linq; +using System.Reactive.Linq; +using DynamicData.Tests.Domain; +using FluentAssertions; +using Xunit; + +namespace DynamicData.Tests.Cache +{ + + public class FilterFixture: IDisposable + { + private readonly ISourceCache _source; + private readonly ChangeSetAggregator _results; + + public FilterFixture() + { + _source = new SourceCache(p => p.Name); + _results = _source.Connect(p => p.Age > 20).AsAggregator(); + } + + public void Dispose() + { + _source.Dispose(); + _results.Dispose(); + } + + [Fact] + public void AddMatched() + { + var person = new Person("Adult1", 50); + _source.AddOrUpdate(person); + + _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + _results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); + _results.Data.Items.First().Should().Be(person, "Should be same person"); + } + + [Fact] + public void AddNotMatched() + { + var person = new Person("Adult1", 10); + _source.AddOrUpdate(person); + + _results.Messages.Count.Should().Be(0, "Should have no item updates"); + _results.Data.Count.Should().Be(0, "Cache should have no items"); + } + + [Fact] + public void AddNotMatchedAndUpdateMatched() + { + const string key = "Adult1"; + var notmatched = new Person(key, 19); + var matched = new Person(key, 21); + + _source.Edit(updater => + { + updater.AddOrUpdate(notmatched); + updater.AddOrUpdate(matched); + }); + + _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + _results.Messages[0].First().Current.Should().Be(matched, "Should be same person"); + _results.Data.Items.First().Should().Be(matched, "Should be same person"); + } + + [Fact] + public void AttemptedRemovalOfANonExistentKeyWillBeIgnored() + { + const string key = "Adult1"; + _source.Remove(key); + _results.Messages.Count.Should().Be(0, "Should be 0 updates"); + } + + [Fact] + public void BatchOfUniqueUpdates() + { + var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); + + _source.AddOrUpdate(people); + _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + _results.Messages[0].Adds.Should().Be(80, "Should return 80 adds"); + + var filtered = people.Where(p => p.Age > 20).OrderBy(p => p.Age).ToArray(); + _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect Filter result"); + } + + [Fact] + public void BatchRemoves() + { + var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + + _source.AddOrUpdate(people); + _source.Remove(people); + + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Adds.Should().Be(80, "Should be 80 addes"); + _results.Messages[1].Removes.Should().Be(80, "Should be 80 removes"); + _results.Data.Count.Should().Be(0, "Should be nothing cached"); + } + + [Fact] + public void BatchSuccessiveUpdates() + { + var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + foreach (var person in people) + { + Person person1 = person; + _source.AddOrUpdate(person1); + } + + _results.Messages.Count.Should().Be(80, "Should be 100 updates"); + _results.Data.Count.Should().Be(80, "Should be 100 in the cache"); + var filtered = people.Where(p => p.Age > 20).OrderBy(p => p.Age).ToArray(); + _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect Filter result"); + } + + [Fact] + public void Clear() + { + var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + _source.AddOrUpdate(people); + _source.Clear(); + + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Adds.Should().Be(80, "Should be 80 addes"); + _results.Messages[1].Removes.Should().Be(80, "Should be 80 removes"); + _results.Data.Count.Should().Be(0, "Should be nothing cached"); + } + + [Fact] + public void Remove() + { + const string key = "Adult1"; + var person = new Person(key, 50); + + _source.AddOrUpdate(person); + _source.Remove(person); + + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Adds.Should().Be(1, "Should be 80 adds"); + _results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); + _results.Data.Count.Should().Be(0, "Should be nothing cached"); + } + + [Fact] + public void UpdateMatched() + + { + const string key = "Adult1"; + var newperson = new Person(key, 50); + var updated = new Person(key, 51); + _source.AddOrUpdate(newperson); + _source.AddOrUpdate(updated); + + _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); + _results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); + } + + [Fact] + public void SameKeyChanges() + { + const string key = "Adult1"; + + _source.Edit(updater => + { + updater.AddOrUpdate(new Person(key, 50)); + updater.AddOrUpdate(new Person(key, 52)); + updater.AddOrUpdate(new Person(key, 53)); + updater.Remove(key); + }); + + _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); + _results.Messages[0].Updates.Should().Be(2, "Should be 2 updates"); + _results.Messages[0].Removes.Should().Be(1, "Should be 1 remove"); + } + + [Fact] + public void UpdateNotMatched() + { + const string key = "Adult1"; + var newperson = new Person(key, 10); + var updated = new Person(key, 11); + + _source.AddOrUpdate(newperson); + _source.AddOrUpdate(updated); + + _results.Messages.Count.Should().Be(0, "Should be no updates"); + _results.Data.Count.Should().Be(0, "Should nothing cached"); + } + + [Fact] + public void DuplicateKeyWithMerge() + { + const string key = "Adult1"; + var newperson = new Person(key, 30); + + using (var results = _source.Connect() + .Merge(_source.Connect()) + .Filter(p => p.Age > 20).AsAggregator()) + { + _source.AddOrUpdate(newperson); // previously this would throw an exception + + results.Messages.Count.Should().Be(2, "Should be 2 messages"); + results.Messages[0].Adds.Should().Be(1, "Should be 1 add"); + results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); + results.Data.Count.Should().Be(1, "Should be cached"); + } + } + } +} diff --git a/DynamicData.Tests/Cache/FilterOnPropertyFixture.cs b/src/DynamicData.Tests/Cache/FilterOnPropertyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/FilterOnPropertyFixture.cs rename to src/DynamicData.Tests/Cache/FilterOnPropertyFixture.cs diff --git a/DynamicData.Tests/Cache/FilterParallelFixture.cs b/src/DynamicData.Tests/Cache/FilterParallelFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/FilterParallelFixture.cs rename to src/DynamicData.Tests/Cache/FilterParallelFixture.cs diff --git a/DynamicData.Tests/Cache/ForEachChangeFixture.cs b/src/DynamicData.Tests/Cache/ForEachChangeFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ForEachChangeFixture.cs rename to src/DynamicData.Tests/Cache/ForEachChangeFixture.cs diff --git a/DynamicData.Tests/Cache/FromAsyncFixture.cs b/src/DynamicData.Tests/Cache/FromAsyncFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/FromAsyncFixture.cs rename to src/DynamicData.Tests/Cache/FromAsyncFixture.cs diff --git a/DynamicData.Tests/Cache/FullJoinFixture.cs b/src/DynamicData.Tests/Cache/FullJoinFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/FullJoinFixture.cs rename to src/DynamicData.Tests/Cache/FullJoinFixture.cs diff --git a/DynamicData.Tests/Cache/FullJoinManyFixture.cs b/src/DynamicData.Tests/Cache/FullJoinManyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/FullJoinManyFixture.cs rename to src/DynamicData.Tests/Cache/FullJoinManyFixture.cs diff --git a/DynamicData.Tests/Cache/GroupControllerFixture.cs b/src/DynamicData.Tests/Cache/GroupControllerFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/GroupControllerFixture.cs rename to src/DynamicData.Tests/Cache/GroupControllerFixture.cs diff --git a/DynamicData.Tests/Cache/GroupControllerForFilteredItemsFixture.cs b/src/DynamicData.Tests/Cache/GroupControllerForFilteredItemsFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/GroupControllerForFilteredItemsFixture.cs rename to src/DynamicData.Tests/Cache/GroupControllerForFilteredItemsFixture.cs diff --git a/DynamicData.Tests/Cache/GroupFixture.cs b/src/DynamicData.Tests/Cache/GroupFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/GroupFixture.cs rename to src/DynamicData.Tests/Cache/GroupFixture.cs diff --git a/DynamicData.Tests/Cache/GroupFromDistinctFixture.cs b/src/DynamicData.Tests/Cache/GroupFromDistinctFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/GroupFromDistinctFixture.cs rename to src/DynamicData.Tests/Cache/GroupFromDistinctFixture.cs diff --git a/DynamicData.Tests/Cache/GroupImmutableFixture.cs b/src/DynamicData.Tests/Cache/GroupImmutableFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/GroupImmutableFixture.cs rename to src/DynamicData.Tests/Cache/GroupImmutableFixture.cs diff --git a/DynamicData.Tests/Cache/GroupOnPropertyFixture.cs b/src/DynamicData.Tests/Cache/GroupOnPropertyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/GroupOnPropertyFixture.cs rename to src/DynamicData.Tests/Cache/GroupOnPropertyFixture.cs diff --git a/DynamicData.Tests/Cache/GroupOnPropertyWithImmutableStateFixture.cs b/src/DynamicData.Tests/Cache/GroupOnPropertyWithImmutableStateFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/GroupOnPropertyWithImmutableStateFixture.cs rename to src/DynamicData.Tests/Cache/GroupOnPropertyWithImmutableStateFixture.cs diff --git a/DynamicData.Tests/Cache/IgnoreUpdateFixture.cs b/src/DynamicData.Tests/Cache/IgnoreUpdateFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/IgnoreUpdateFixture.cs rename to src/DynamicData.Tests/Cache/IgnoreUpdateFixture.cs diff --git a/DynamicData.Tests/Cache/IncludeUpdateFixture.cs b/src/DynamicData.Tests/Cache/IncludeUpdateFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/IncludeUpdateFixture.cs rename to src/DynamicData.Tests/Cache/IncludeUpdateFixture.cs diff --git a/DynamicData.Tests/Cache/InnerJoinFixture.cs b/src/DynamicData.Tests/Cache/InnerJoinFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/InnerJoinFixture.cs rename to src/DynamicData.Tests/Cache/InnerJoinFixture.cs diff --git a/DynamicData.Tests/Cache/InnerJoinManyFixture.cs b/src/DynamicData.Tests/Cache/InnerJoinManyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/InnerJoinManyFixture.cs rename to src/DynamicData.Tests/Cache/InnerJoinManyFixture.cs diff --git a/DynamicData.Tests/Cache/KeyValueCollectionEx.cs b/src/DynamicData.Tests/Cache/KeyValueCollectionEx.cs similarity index 100% rename from DynamicData.Tests/Cache/KeyValueCollectionEx.cs rename to src/DynamicData.Tests/Cache/KeyValueCollectionEx.cs diff --git a/DynamicData.Tests/Cache/LeftJoinFixture.cs b/src/DynamicData.Tests/Cache/LeftJoinFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/LeftJoinFixture.cs rename to src/DynamicData.Tests/Cache/LeftJoinFixture.cs diff --git a/DynamicData.Tests/Cache/LeftJoinManyFixture.cs b/src/DynamicData.Tests/Cache/LeftJoinManyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/LeftJoinManyFixture.cs rename to src/DynamicData.Tests/Cache/LeftJoinManyFixture.cs diff --git a/DynamicData.Tests/Cache/MergeManyFixture.cs b/src/DynamicData.Tests/Cache/MergeManyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/MergeManyFixture.cs rename to src/DynamicData.Tests/Cache/MergeManyFixture.cs diff --git a/DynamicData.Tests/Cache/MergeManyItemsFixture.cs b/src/DynamicData.Tests/Cache/MergeManyItemsFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/MergeManyItemsFixture.cs rename to src/DynamicData.Tests/Cache/MergeManyItemsFixture.cs diff --git a/DynamicData.Tests/Cache/MergeManyWithKeyOverloadFixture.cs b/src/DynamicData.Tests/Cache/MergeManyWithKeyOverloadFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/MergeManyWithKeyOverloadFixture.cs rename to src/DynamicData.Tests/Cache/MergeManyWithKeyOverloadFixture.cs diff --git a/DynamicData.Tests/Cache/MonitorStatusFixture.cs b/src/DynamicData.Tests/Cache/MonitorStatusFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/MonitorStatusFixture.cs rename to src/DynamicData.Tests/Cache/MonitorStatusFixture.cs diff --git a/DynamicData.Tests/Cache/ObservableCachePreviewFixture.cs b/src/DynamicData.Tests/Cache/ObservableCachePreviewFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ObservableCachePreviewFixture.cs rename to src/DynamicData.Tests/Cache/ObservableCachePreviewFixture.cs diff --git a/DynamicData.Tests/Cache/ObservableChangeSetFixture.cs b/src/DynamicData.Tests/Cache/ObservableChangeSetFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ObservableChangeSetFixture.cs rename to src/DynamicData.Tests/Cache/ObservableChangeSetFixture.cs diff --git a/DynamicData.Tests/Cache/ObservableToObservableChangeSetFixture.cs b/src/DynamicData.Tests/Cache/ObservableToObservableChangeSetFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ObservableToObservableChangeSetFixture.cs rename to src/DynamicData.Tests/Cache/ObservableToObservableChangeSetFixture.cs diff --git a/DynamicData.Tests/Cache/OnItemFixture.cs b/src/DynamicData.Tests/Cache/OnItemFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/OnItemFixture.cs rename to src/DynamicData.Tests/Cache/OnItemFixture.cs diff --git a/DynamicData.Tests/Cache/OrFixture.cs b/src/DynamicData.Tests/Cache/OrFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/OrFixture.cs rename to src/DynamicData.Tests/Cache/OrFixture.cs diff --git a/DynamicData.Tests/Cache/PageFixture.cs b/src/DynamicData.Tests/Cache/PageFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/PageFixture.cs rename to src/DynamicData.Tests/Cache/PageFixture.cs diff --git a/DynamicData.Tests/Cache/QueryWhenChangedFixture.cs b/src/DynamicData.Tests/Cache/QueryWhenChangedFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/QueryWhenChangedFixture.cs rename to src/DynamicData.Tests/Cache/QueryWhenChangedFixture.cs diff --git a/DynamicData.Tests/Cache/RefCountFixture.cs b/src/DynamicData.Tests/Cache/RefCountFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/RefCountFixture.cs rename to src/DynamicData.Tests/Cache/RefCountFixture.cs diff --git a/DynamicData.Tests/Cache/RightJoinFixture.cs b/src/DynamicData.Tests/Cache/RightJoinFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/RightJoinFixture.cs rename to src/DynamicData.Tests/Cache/RightJoinFixture.cs diff --git a/DynamicData.Tests/Cache/RightJoinManyFixture.cs b/src/DynamicData.Tests/Cache/RightJoinManyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/RightJoinManyFixture.cs rename to src/DynamicData.Tests/Cache/RightJoinManyFixture.cs diff --git a/DynamicData.Tests/Cache/SizeLimitFixture.cs b/src/DynamicData.Tests/Cache/SizeLimitFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/SizeLimitFixture.cs rename to src/DynamicData.Tests/Cache/SizeLimitFixture.cs diff --git a/DynamicData.Tests/Cache/SortFixture.cs b/src/DynamicData.Tests/Cache/SortFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/SortFixture.cs rename to src/DynamicData.Tests/Cache/SortFixture.cs diff --git a/DynamicData.Tests/Cache/SortObservableFixtureFixture.cs b/src/DynamicData.Tests/Cache/SortObservableFixtureFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/SortObservableFixtureFixture.cs rename to src/DynamicData.Tests/Cache/SortObservableFixtureFixture.cs diff --git a/DynamicData.Tests/Cache/SourceCacheFixture.cs b/src/DynamicData.Tests/Cache/SourceCacheFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/SourceCacheFixture.cs rename to src/DynamicData.Tests/Cache/SourceCacheFixture.cs diff --git a/DynamicData.Tests/Cache/SubscribeManyFixture.cs b/src/DynamicData.Tests/Cache/SubscribeManyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/SubscribeManyFixture.cs rename to src/DynamicData.Tests/Cache/SubscribeManyFixture.cs diff --git a/DynamicData.Tests/Cache/SwitchFixture.cs b/src/DynamicData.Tests/Cache/SwitchFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/SwitchFixture.cs rename to src/DynamicData.Tests/Cache/SwitchFixture.cs diff --git a/DynamicData.Tests/Cache/TimeExpiryFixture.cs b/src/DynamicData.Tests/Cache/TimeExpiryFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TimeExpiryFixture.cs rename to src/DynamicData.Tests/Cache/TimeExpiryFixture.cs diff --git a/DynamicData.Tests/Cache/ToObservableChangeSetFixture.cs b/src/DynamicData.Tests/Cache/ToObservableChangeSetFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ToObservableChangeSetFixture.cs rename to src/DynamicData.Tests/Cache/ToObservableChangeSetFixture.cs diff --git a/DynamicData.Tests/Cache/ToObservableChangeSetFixtureWithCompletion.cs b/src/DynamicData.Tests/Cache/ToObservableChangeSetFixtureWithCompletion.cs similarity index 100% rename from DynamicData.Tests/Cache/ToObservableChangeSetFixtureWithCompletion.cs rename to src/DynamicData.Tests/Cache/ToObservableChangeSetFixtureWithCompletion.cs diff --git a/DynamicData.Tests/Cache/ToSortedCollectionFixture.cs b/src/DynamicData.Tests/Cache/ToSortedCollectionFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/ToSortedCollectionFixture.cs rename to src/DynamicData.Tests/Cache/ToSortedCollectionFixture.cs diff --git a/DynamicData.Tests/Cache/TransformAsyncFixture.cs b/src/DynamicData.Tests/Cache/TransformAsyncFixture.cs similarity index 97% rename from DynamicData.Tests/Cache/TransformAsyncFixture.cs rename to src/DynamicData.Tests/Cache/TransformAsyncFixture.cs index ef200b152..4661aac17 100644 --- a/DynamicData.Tests/Cache/TransformAsyncFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformAsyncFixture.cs @@ -1,272 +1,272 @@ -using System; -using System.Reactive; -using System.Reactive.Linq; -using System.Threading.Tasks; -using DynamicData.Tests.Domain; - - -namespace DynamicData.Tests.Cache -{ - [Obsolete("Not obsolete - test commented out due to test run freezing on Appveyor")] - public class TransformAsyncFixture - { - //[Fact] - //public void ReTransformAll() - //{ - // var people = Enumerable.Range(1, 10).Select(i => new Person("Name" + i, i)).ToArray(); - // var forceTransform = new Subject(); - - // using (var stub = new TransformStub(forceTransform)) - // { - // stub.Source.AddOrUpdate(people); - // forceTransform.OnNext(Unit.Default); - - // stub.Results.Messages.Count.Should().Be(2); - // stub.Results.Messages[1].Updates.Should().Be(10); - - // for (int i = 1; i <= 10; i++) - // { - // var original = stub.Results.Messages[0].ElementAt(i - 1).Current; - // var updated = stub.Results.Messages[1].ElementAt(i - 1).Current; - - // updated.Should().Be(original); - // ReferenceEquals(original, updated).Should().BeFalse(); - // } - // } - //} - - //[Fact] - //public void ReTransformSelected() - //{ - // var people = Enumerable.Range(1, 10).Select(i => new Person("Name" + i, i)).ToArray(); - // var forceTransform = new Subject>(); - - // using (var stub = new TransformStub(forceTransform)) - // { - // stub.Source.AddOrUpdate(people); - // forceTransform.OnNext(person => person.Age <= 5); - - // stub.Results.Messages.Count.Should().Be(2); - // stub.Results.Messages[1].Updates.Should().Be(5); - - // for (int i = 1; i <= 5; i++) - // { - // var original = stub.Results.Messages[0].ElementAt(i - 1).Current; - // var updated = stub.Results.Messages[1].ElementAt(i - 1).Current; - // updated.Should().Be(original); - // ReferenceEquals(original, updated).Should().BeFalse(); - // } - // } - //} - - //[Fact] - //public async Task Add() - //{ - // using (var stub = new TransformStub()) - // { - // var person = new Person("Adult1", 50); - // stub.Source.AddOrUpdate(person); - - // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // stub.Results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); - - // var firstPerson = await stub.TransformFactory(person); - - // stub.Results.Data.Items.First().Should().Be(firstPerson, "Should be same person"); - // } - //} - - //[Fact] - //public void Remove() - //{ - // const string key = "Adult1"; - // var person = new Person(key, 50); - - // using (var stub = new TransformStub()) - // { - // stub.Source.AddOrUpdate(person); - // stub.Source.Remove(key); - - // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // stub.Results.Messages[0].Adds.Should().Be(1, "Should be 80 addes"); - // stub.Results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); - // stub.Results.Data.Count.Should().Be(0, "Should be nothing cached"); - // } - //} - - //[Fact] - //public void Update() - //{ - // const string key = "Adult1"; - // var newperson = new Person(key, 50); - // var updated = new Person(key, 51); - - // using (var stub = new TransformStub()) - // { - // stub.Source.AddOrUpdate(newperson); - // stub.Source.AddOrUpdate(updated); - - // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // stub.Results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); - // stub.Results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); - // } - //} - - //[Fact] - //public async Task BatchOfUniqueUpdates() - //{ - // var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - // using (var stub = new TransformStub()) - // { - // stub.Source.AddOrUpdate(people); - - // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // stub.Results.Messages[0].Adds.Should().Be(100, "Should return 100 adds"); - - // var result = await Task.WhenAll(people.Select(stub.TransformFactory)); - // var transformed = result.OrderBy(p => p.Age).ToArray(); - // stub.Results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(stub.Results.Data.Items.OrderBy(p => p.Age), "Incorrect transform result"); - // } - //} - - //[Fact] - //public async Task SameKeyChanges() - //{ - // using (var stub = new TransformStub()) - // { - // var people = Enumerable.Range(1, 10).Select(i => new Person("Name", i)).ToArray(); - - // stub.Source.AddOrUpdate(people); - - // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // stub.Results.Messages[0].Adds.Should().Be(1, "Should return 1 adds"); - // stub.Results.Messages[0].Updates.Should().Be(9, "Should return 9 adds"); - // stub.Results.Data.Count.Should().Be(1, "Should result in 1 record"); - - // var lastTransformed = await stub.TransformFactory(people.Last()); - // var onlyItemInCache = stub.Results.Data.Items.First(); - - // onlyItemInCache.Should().Be(lastTransformed, "Incorrect transform result"); - // } - //} - - //[Fact] - //public void Clear() - //{ - // using (var stub = new TransformStub()) - // { - // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - - // stub.Source.AddOrUpdate(people); - // stub.Source.Clear(); - - // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // stub.Results.Messages[0].Adds.Should().Be(100, "Should be 80 addes"); - // stub.Results.Messages[1].Removes.Should().Be(100, "Should be 80 removes"); - // stub.Results.Data.Count.Should().Be(0, "Should be nothing cached"); - // } - //} - - - //[Fact] - //public void HandleError() - //{ - // using (var stub = new TransformStub(p => - // { - // throw new Exception("Broken"); - // })) - // { - // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - // stub.Source.AddOrUpdate(people); - - // stub.Results.Error.Should().NotBeNull(); - - // Exception error = null; - // stub.Source.Connect() - // .Subscribe(changes => { }, ex => error = ex); - - - // error.Should().NotBeNull(); - - // } - //} - - private class TransformStub : IDisposable - { - public ISourceCache Source { get; } = new SourceCache(p => p.Name); - public ChangeSetAggregator Results { get; } - - public Func> TransformFactory { get; } - - public TransformStub() - { - TransformFactory = (p) => - { - var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); - return Task.FromResult(result); - }; - - Results = new ChangeSetAggregator - ( - Source.Connect().TransformAsync(TransformFactory) - ); - } - public TransformStub(Func factory) - { - TransformFactory = (p) => - { - var result = factory(p); - return Task.FromResult(result); - }; - - Results = new ChangeSetAggregator - ( - Source.Connect().TransformAsync(TransformFactory) - ); - } - - public TransformStub(IObservable retransformer) - { - TransformFactory = (p) => - { - var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); - return Task.FromResult(result); - }; - - Results = new ChangeSetAggregator - ( - Source.Connect().TransformAsync(TransformFactory, retransformer.Select(x => - { - Func transformer = (p, key) => true; - return transformer; - })) - ); - } - - public TransformStub(IObservable> retransformer) - { - TransformFactory = (p) => - { - var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); - return Task.FromResult(result); - }; - - Results = new ChangeSetAggregator - ( - Source.Connect().TransformAsync(TransformFactory, retransformer.Select(selector => - { - Func transformed = (p, key) => selector(p); - return transformed; - })) - ); - } - - public void Dispose() - { - Source.Dispose(); - Results.Dispose(); - } - } - } +using System; +using System.Reactive; +using System.Reactive.Linq; +using System.Threading.Tasks; +using DynamicData.Tests.Domain; + + +namespace DynamicData.Tests.Cache +{ + [Obsolete("Not obsolete - test commented out due to test run freezing on Appveyor")] + public class TransformAsyncFixture + { + //[Fact] + //public void ReTransformAll() + //{ + // var people = Enumerable.Range(1, 10).Select(i => new Person("Name" + i, i)).ToArray(); + // var forceTransform = new Subject(); + + // using (var stub = new TransformStub(forceTransform)) + // { + // stub.Source.AddOrUpdate(people); + // forceTransform.OnNext(Unit.Default); + + // stub.Results.Messages.Count.Should().Be(2); + // stub.Results.Messages[1].Updates.Should().Be(10); + + // for (int i = 1; i <= 10; i++) + // { + // var original = stub.Results.Messages[0].ElementAt(i - 1).Current; + // var updated = stub.Results.Messages[1].ElementAt(i - 1).Current; + + // updated.Should().Be(original); + // ReferenceEquals(original, updated).Should().BeFalse(); + // } + // } + //} + + //[Fact] + //public void ReTransformSelected() + //{ + // var people = Enumerable.Range(1, 10).Select(i => new Person("Name" + i, i)).ToArray(); + // var forceTransform = new Subject>(); + + // using (var stub = new TransformStub(forceTransform)) + // { + // stub.Source.AddOrUpdate(people); + // forceTransform.OnNext(person => person.Age <= 5); + + // stub.Results.Messages.Count.Should().Be(2); + // stub.Results.Messages[1].Updates.Should().Be(5); + + // for (int i = 1; i <= 5; i++) + // { + // var original = stub.Results.Messages[0].ElementAt(i - 1).Current; + // var updated = stub.Results.Messages[1].ElementAt(i - 1).Current; + // updated.Should().Be(original); + // ReferenceEquals(original, updated).Should().BeFalse(); + // } + // } + //} + + //[Fact] + //public async Task Add() + //{ + // using (var stub = new TransformStub()) + // { + // var person = new Person("Adult1", 50); + // stub.Source.AddOrUpdate(person); + + // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // stub.Results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); + + // var firstPerson = await stub.TransformFactory(person); + + // stub.Results.Data.Items.First().Should().Be(firstPerson, "Should be same person"); + // } + //} + + //[Fact] + //public void Remove() + //{ + // const string key = "Adult1"; + // var person = new Person(key, 50); + + // using (var stub = new TransformStub()) + // { + // stub.Source.AddOrUpdate(person); + // stub.Source.Remove(key); + + // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // stub.Results.Messages[0].Adds.Should().Be(1, "Should be 80 addes"); + // stub.Results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); + // stub.Results.Data.Count.Should().Be(0, "Should be nothing cached"); + // } + //} + + //[Fact] + //public void Update() + //{ + // const string key = "Adult1"; + // var newperson = new Person(key, 50); + // var updated = new Person(key, 51); + + // using (var stub = new TransformStub()) + // { + // stub.Source.AddOrUpdate(newperson); + // stub.Source.AddOrUpdate(updated); + + // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // stub.Results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); + // stub.Results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); + // } + //} + + //[Fact] + //public async Task BatchOfUniqueUpdates() + //{ + // var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); + // using (var stub = new TransformStub()) + // { + // stub.Source.AddOrUpdate(people); + + // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // stub.Results.Messages[0].Adds.Should().Be(100, "Should return 100 adds"); + + // var result = await Task.WhenAll(people.Select(stub.TransformFactory)); + // var transformed = result.OrderBy(p => p.Age).ToArray(); + // stub.Results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(stub.Results.Data.Items.OrderBy(p => p.Age), "Incorrect transform result"); + // } + //} + + //[Fact] + //public async Task SameKeyChanges() + //{ + // using (var stub = new TransformStub()) + // { + // var people = Enumerable.Range(1, 10).Select(i => new Person("Name", i)).ToArray(); + + // stub.Source.AddOrUpdate(people); + + // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // stub.Results.Messages[0].Adds.Should().Be(1, "Should return 1 adds"); + // stub.Results.Messages[0].Updates.Should().Be(9, "Should return 9 adds"); + // stub.Results.Data.Count.Should().Be(1, "Should result in 1 record"); + + // var lastTransformed = await stub.TransformFactory(people.Last()); + // var onlyItemInCache = stub.Results.Data.Items.First(); + + // onlyItemInCache.Should().Be(lastTransformed, "Incorrect transform result"); + // } + //} + + //[Fact] + //public void Clear() + //{ + // using (var stub = new TransformStub()) + // { + // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + + // stub.Source.AddOrUpdate(people); + // stub.Source.Clear(); + + // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // stub.Results.Messages[0].Adds.Should().Be(100, "Should be 80 addes"); + // stub.Results.Messages[1].Removes.Should().Be(100, "Should be 80 removes"); + // stub.Results.Data.Count.Should().Be(0, "Should be nothing cached"); + // } + //} + + + //[Fact] + //public void HandleError() + //{ + // using (var stub = new TransformStub(p => + // { + // throw new Exception("Broken"); + // })) + // { + // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + // stub.Source.AddOrUpdate(people); + + // stub.Results.Error.Should().NotBeNull(); + + // Exception error = null; + // stub.Source.Connect() + // .Subscribe(changes => { }, ex => error = ex); + + + // error.Should().NotBeNull(); + + // } + //} + + private class TransformStub : IDisposable + { + public ISourceCache Source { get; } = new SourceCache(p => p.Name); + public ChangeSetAggregator Results { get; } + + public Func> TransformFactory { get; } + + public TransformStub() + { + TransformFactory = (p) => + { + var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); + return Task.FromResult(result); + }; + + Results = new ChangeSetAggregator + ( + Source.Connect().TransformAsync(TransformFactory) + ); + } + public TransformStub(Func factory) + { + TransformFactory = (p) => + { + var result = factory(p); + return Task.FromResult(result); + }; + + Results = new ChangeSetAggregator + ( + Source.Connect().TransformAsync(TransformFactory) + ); + } + + public TransformStub(IObservable retransformer) + { + TransformFactory = (p) => + { + var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); + return Task.FromResult(result); + }; + + Results = new ChangeSetAggregator + ( + Source.Connect().TransformAsync(TransformFactory, retransformer.Select(x => + { + Func transformer = (p, key) => true; + return transformer; + })) + ); + } + + public TransformStub(IObservable> retransformer) + { + TransformFactory = (p) => + { + var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); + return Task.FromResult(result); + }; + + Results = new ChangeSetAggregator + ( + Source.Connect().TransformAsync(TransformFactory, retransformer.Select(selector => + { + Func transformed = (p, key) => selector(p); + return transformed; + })) + ); + } + + public void Dispose() + { + Source.Dispose(); + Results.Dispose(); + } + } + } } \ No newline at end of file diff --git a/DynamicData.Tests/Cache/TransformFixture.cs b/src/DynamicData.Tests/Cache/TransformFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformFixture.cs rename to src/DynamicData.Tests/Cache/TransformFixture.cs diff --git a/DynamicData.Tests/Cache/TransformFixtureParallel.cs b/src/DynamicData.Tests/Cache/TransformFixtureParallel.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformFixtureParallel.cs rename to src/DynamicData.Tests/Cache/TransformFixtureParallel.cs diff --git a/DynamicData.Tests/Cache/TransformManyFixture.cs b/src/DynamicData.Tests/Cache/TransformManyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformManyFixture.cs rename to src/DynamicData.Tests/Cache/TransformManyFixture.cs diff --git a/DynamicData.Tests/Cache/TransformManyObservableCacheFixture.cs b/src/DynamicData.Tests/Cache/TransformManyObservableCacheFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformManyObservableCacheFixture.cs rename to src/DynamicData.Tests/Cache/TransformManyObservableCacheFixture.cs diff --git a/DynamicData.Tests/Cache/TransformManyRefreshFixture.cs b/src/DynamicData.Tests/Cache/TransformManyRefreshFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformManyRefreshFixture.cs rename to src/DynamicData.Tests/Cache/TransformManyRefreshFixture.cs diff --git a/DynamicData.Tests/Cache/TransformManySimpleFixture.cs b/src/DynamicData.Tests/Cache/TransformManySimpleFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformManySimpleFixture.cs rename to src/DynamicData.Tests/Cache/TransformManySimpleFixture.cs diff --git a/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs b/src/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs similarity index 97% rename from DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs rename to src/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs index c72669f5c..9504de333 100644 --- a/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs @@ -1,291 +1,291 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reactive; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using System.Threading.Tasks; -using DynamicData.Kernel; -using DynamicData.Tests.Domain; -using FluentAssertions; -using Xunit; - -namespace DynamicData.Tests.Cache -{ - [Obsolete("Not obsolete - test commented out due to test run freezing on Appveyor")] - public class TransformSafeAsyncFixture - { - [Fact] - public void ReTransformAll() - { - var people = Enumerable.Range(1, 10).Select(i => new Person("Name" + i, i)).ToArray(); - var forceTransform = new Subject(); - - using (var stub = new TransformStub(forceTransform)) - { - stub.Source.AddOrUpdate(people); - forceTransform.OnNext(Unit.Default); - - stub.Results.Messages.Count.Should().Be(2); - stub.Results.Messages[1].Updates.Should().Be(10); - - for (int i = 1; i <= 10; i++) - { - var original = stub.Results.Messages[0].ElementAt(i - 1).Current; - var updated = stub.Results.Messages[1].ElementAt(i - 1).Current; - - updated.Should().Be(original); - ReferenceEquals(original, updated).Should().BeFalse(); - } - } - } - - //[Fact] - //public void ReTransformSelected() - //{ - // var people = Enumerable.Range(1, 10).Select(i => new Person("Name" + i, i)).ToArray(); - // var forceTransform = new Subject>(); - - // using (var stub = new TransformStub(forceTransform)) - // { - // stub.Source.AddOrUpdate(people); - // forceTransform.OnNext(person => person.Age <= 5); - - // stub.Results.Messages.Count.Should().Be(2); - // stub.Results.Messages[1].Updates.Should().Be(5); - - // for (int i = 1; i <= 5; i++) - // { - // var original = stub.Results.Messages[0].ElementAt(i - 1).Current; - // var updated = stub.Results.Messages[1].ElementAt(i - 1).Current; - // updated.Should().Be(original); - // ReferenceEquals(original, updated).Should().BeFalse(); - // } - // } - //} - - //[Fact] - //public async Task Add() - //{ - // using (var stub = new TransformStub()) - // { - // var person = new Person("Adult1", 50); - // stub.Source.AddOrUpdate(person); - - // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // stub.Results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); - - // var firstPerson = await stub.TransformFactory(person); - - // stub.Results.Data.Items.First().Should().Be(firstPerson, "Should be same person"); - // } - //} - - //[Fact] - //public void Remove() - //{ - // const string key = "Adult1"; - // var person = new Person(key, 50); - - // using (var stub = new TransformStub()) - // { - // stub.Source.AddOrUpdate(person); - // stub.Source.Remove(key); - - // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // stub.Results.Messages[0].Adds.Should().Be(1, "Should be 80 addes"); - // stub.Results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); - // stub.Results.Data.Count.Should().Be(0, "Should be nothing cached"); - // } - //} - - //[Fact] - //public void Update() - //{ - // const string key = "Adult1"; - // var newperson = new Person(key, 50); - // var updated = new Person(key, 51); - - // using (var stub = new TransformStub()) - // { - // stub.Source.AddOrUpdate(newperson); - // stub.Source.AddOrUpdate(updated); - - // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // stub.Results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); - // stub.Results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); - // } - //} - - //[Fact] - //public async Task BatchOfUniqueUpdates() - //{ - // var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - // using (var stub = new TransformStub()) - // { - // stub.Source.AddOrUpdate(people); - - // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // stub.Results.Messages[0].Adds.Should().Be(100, "Should return 100 adds"); - - // var result = await Task.WhenAll(people.Select(stub.TransformFactory)); - // var transformed = result.OrderBy(p => p.Age).ToArray(); - // stub.Results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(stub.Results.Data.Items.OrderBy(p => p.Age), "Incorrect transform result"); - // } - //} - - //[Fact] - //public async Task SameKeyChanges() - //{ - // using (var stub = new TransformStub()) - // { - // var people = Enumerable.Range(1, 10).Select(i => new Person("Name", i)).ToArray(); - - // stub.Source.AddOrUpdate(people); - - // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // stub.Results.Messages[0].Adds.Should().Be(1, "Should return 1 adds"); - // stub.Results.Messages[0].Updates.Should().Be(9, "Should return 9 adds"); - // stub.Results.Data.Count.Should().Be(1, "Should result in 1 record"); - - // var lastTransformed = await stub.TransformFactory(people.Last()); - // var onlyItemInCache = stub.Results.Data.Items.First(); - - // onlyItemInCache.Should().Be(lastTransformed, "Incorrect transform result"); - // } - //} - - //[Fact] - //public void Clear() - //{ - // using (var stub = new TransformStub()) - // { - // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - - // stub.Source.AddOrUpdate(people); - // stub.Source.Clear(); - - // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // stub.Results.Messages[0].Adds.Should().Be(100, "Should be 80 addes"); - // stub.Results.Messages[1].Removes.Should().Be(100, "Should be 80 removes"); - // stub.Results.Data.Count.Should().Be(0, "Should be nothing cached"); - // } - //} - - - //[Fact] - //public void HandleError() - //{ - // using (var stub = new TransformStub(p => - // { - // if (p.Age <= 50) - // return new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); - - // throw new Exception("Broken"); - // })) - // { - // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - // stub.Source.AddOrUpdate(people); - - // stub.Results.Error.Should().BeNull(); - - // Exception error = null; - // stub.Source.Connect() - // .Subscribe(changes => { }, ex => error = ex); - - - // error.Should().BeNull(); - - // stub.HandledErrors.Count.Should().Be(50); - // stub.Results.Data.Count.Should().Be(50); - // } - //} - - private class TransformStub : IDisposable - { - public ISourceCache Source { get; } = new SourceCache(p => p.Name); - public ChangeSetAggregator Results { get; } - - public Func< Person, Task> TransformFactory { get; } - - public IList> HandledErrors { get; } = new List>(); - - public TransformStub() - { - TransformFactory = (p) => - { - var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); - return Task.FromResult(result); - }; - - Results = new ChangeSetAggregator - ( - Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler) - ); - } - - - public TransformStub(Func factory) - { - TransformFactory = (p) => - { - var result = factory(p); - return Task.FromResult(result); - }; - - Results = new ChangeSetAggregator - ( - Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler) - ); - } - - public TransformStub(IObservable retransformer) - { - TransformFactory = (p) => - { - var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); - return Task.FromResult(result); - }; - - Results = new ChangeSetAggregator - ( - Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler, retransformer.Select(x => - { - bool Transformer(Person p, string key) => true; - return (Func) Transformer; - })) - ); - } - - public TransformStub(IObservable> retransformer) - { - TransformFactory = (p) => - { - var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); - return Task.FromResult(result); - }; - - Results = new ChangeSetAggregator - ( - Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler, retransformer.Select(selector => - { - bool Transformed(Person p, string key) => selector(p); - return (Func) Transformed; - })) - ); - } - - private void ErrorHandler(Error error) - { - HandledErrors.Add(error); - } - - public void Dispose() - { - Source.Dispose(); - Results.Dispose(); - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading.Tasks; +using DynamicData.Kernel; +using DynamicData.Tests.Domain; +using FluentAssertions; +using Xunit; + +namespace DynamicData.Tests.Cache +{ + [Obsolete("Not obsolete - test commented out due to test run freezing on Appveyor")] + public class TransformSafeAsyncFixture + { + [Fact] + public void ReTransformAll() + { + var people = Enumerable.Range(1, 10).Select(i => new Person("Name" + i, i)).ToArray(); + var forceTransform = new Subject(); + + using (var stub = new TransformStub(forceTransform)) + { + stub.Source.AddOrUpdate(people); + forceTransform.OnNext(Unit.Default); + + stub.Results.Messages.Count.Should().Be(2); + stub.Results.Messages[1].Updates.Should().Be(10); + + for (int i = 1; i <= 10; i++) + { + var original = stub.Results.Messages[0].ElementAt(i - 1).Current; + var updated = stub.Results.Messages[1].ElementAt(i - 1).Current; + + updated.Should().Be(original); + ReferenceEquals(original, updated).Should().BeFalse(); + } + } + } + + //[Fact] + //public void ReTransformSelected() + //{ + // var people = Enumerable.Range(1, 10).Select(i => new Person("Name" + i, i)).ToArray(); + // var forceTransform = new Subject>(); + + // using (var stub = new TransformStub(forceTransform)) + // { + // stub.Source.AddOrUpdate(people); + // forceTransform.OnNext(person => person.Age <= 5); + + // stub.Results.Messages.Count.Should().Be(2); + // stub.Results.Messages[1].Updates.Should().Be(5); + + // for (int i = 1; i <= 5; i++) + // { + // var original = stub.Results.Messages[0].ElementAt(i - 1).Current; + // var updated = stub.Results.Messages[1].ElementAt(i - 1).Current; + // updated.Should().Be(original); + // ReferenceEquals(original, updated).Should().BeFalse(); + // } + // } + //} + + //[Fact] + //public async Task Add() + //{ + // using (var stub = new TransformStub()) + // { + // var person = new Person("Adult1", 50); + // stub.Source.AddOrUpdate(person); + + // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // stub.Results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); + + // var firstPerson = await stub.TransformFactory(person); + + // stub.Results.Data.Items.First().Should().Be(firstPerson, "Should be same person"); + // } + //} + + //[Fact] + //public void Remove() + //{ + // const string key = "Adult1"; + // var person = new Person(key, 50); + + // using (var stub = new TransformStub()) + // { + // stub.Source.AddOrUpdate(person); + // stub.Source.Remove(key); + + // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // stub.Results.Messages[0].Adds.Should().Be(1, "Should be 80 addes"); + // stub.Results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); + // stub.Results.Data.Count.Should().Be(0, "Should be nothing cached"); + // } + //} + + //[Fact] + //public void Update() + //{ + // const string key = "Adult1"; + // var newperson = new Person(key, 50); + // var updated = new Person(key, 51); + + // using (var stub = new TransformStub()) + // { + // stub.Source.AddOrUpdate(newperson); + // stub.Source.AddOrUpdate(updated); + + // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // stub.Results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); + // stub.Results.Messages[1].Updates.Should().Be(1, "Should be 1 update"); + // } + //} + + //[Fact] + //public async Task BatchOfUniqueUpdates() + //{ + // var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); + // using (var stub = new TransformStub()) + // { + // stub.Source.AddOrUpdate(people); + + // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // stub.Results.Messages[0].Adds.Should().Be(100, "Should return 100 adds"); + + // var result = await Task.WhenAll(people.Select(stub.TransformFactory)); + // var transformed = result.OrderBy(p => p.Age).ToArray(); + // stub.Results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(stub.Results.Data.Items.OrderBy(p => p.Age), "Incorrect transform result"); + // } + //} + + //[Fact] + //public async Task SameKeyChanges() + //{ + // using (var stub = new TransformStub()) + // { + // var people = Enumerable.Range(1, 10).Select(i => new Person("Name", i)).ToArray(); + + // stub.Source.AddOrUpdate(people); + + // stub.Results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // stub.Results.Messages[0].Adds.Should().Be(1, "Should return 1 adds"); + // stub.Results.Messages[0].Updates.Should().Be(9, "Should return 9 adds"); + // stub.Results.Data.Count.Should().Be(1, "Should result in 1 record"); + + // var lastTransformed = await stub.TransformFactory(people.Last()); + // var onlyItemInCache = stub.Results.Data.Items.First(); + + // onlyItemInCache.Should().Be(lastTransformed, "Incorrect transform result"); + // } + //} + + //[Fact] + //public void Clear() + //{ + // using (var stub = new TransformStub()) + // { + // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + + // stub.Source.AddOrUpdate(people); + // stub.Source.Clear(); + + // stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // stub.Results.Messages[0].Adds.Should().Be(100, "Should be 80 addes"); + // stub.Results.Messages[1].Removes.Should().Be(100, "Should be 80 removes"); + // stub.Results.Data.Count.Should().Be(0, "Should be nothing cached"); + // } + //} + + + //[Fact] + //public void HandleError() + //{ + // using (var stub = new TransformStub(p => + // { + // if (p.Age <= 50) + // return new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); + + // throw new Exception("Broken"); + // })) + // { + // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + // stub.Source.AddOrUpdate(people); + + // stub.Results.Error.Should().BeNull(); + + // Exception error = null; + // stub.Source.Connect() + // .Subscribe(changes => { }, ex => error = ex); + + + // error.Should().BeNull(); + + // stub.HandledErrors.Count.Should().Be(50); + // stub.Results.Data.Count.Should().Be(50); + // } + //} + + private class TransformStub : IDisposable + { + public ISourceCache Source { get; } = new SourceCache(p => p.Name); + public ChangeSetAggregator Results { get; } + + public Func< Person, Task> TransformFactory { get; } + + public IList> HandledErrors { get; } = new List>(); + + public TransformStub() + { + TransformFactory = (p) => + { + var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); + return Task.FromResult(result); + }; + + Results = new ChangeSetAggregator + ( + Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler) + ); + } + + + public TransformStub(Func factory) + { + TransformFactory = (p) => + { + var result = factory(p); + return Task.FromResult(result); + }; + + Results = new ChangeSetAggregator + ( + Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler) + ); + } + + public TransformStub(IObservable retransformer) + { + TransformFactory = (p) => + { + var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); + return Task.FromResult(result); + }; + + Results = new ChangeSetAggregator + ( + Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler, retransformer.Select(x => + { + bool Transformer(Person p, string key) => true; + return (Func) Transformer; + })) + ); + } + + public TransformStub(IObservable> retransformer) + { + TransformFactory = (p) => + { + var result = new PersonWithGender(p, p.Age % 2 == 0 ? "M" : "F"); + return Task.FromResult(result); + }; + + Results = new ChangeSetAggregator + ( + Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler, retransformer.Select(selector => + { + bool Transformed(Person p, string key) => selector(p); + return (Func) Transformed; + })) + ); + } + + private void ErrorHandler(Error error) + { + HandledErrors.Add(error); + } + + public void Dispose() + { + Source.Dispose(); + Results.Dispose(); + } + } + } } \ No newline at end of file diff --git a/DynamicData.Tests/Cache/TransformSafeFixture.cs b/src/DynamicData.Tests/Cache/TransformSafeFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformSafeFixture.cs rename to src/DynamicData.Tests/Cache/TransformSafeFixture.cs diff --git a/DynamicData.Tests/Cache/TransformSafeParallelFixture.cs b/src/DynamicData.Tests/Cache/TransformSafeParallelFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformSafeParallelFixture.cs rename to src/DynamicData.Tests/Cache/TransformSafeParallelFixture.cs diff --git a/DynamicData.Tests/Cache/TransformTreeFixture.cs b/src/DynamicData.Tests/Cache/TransformTreeFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformTreeFixture.cs rename to src/DynamicData.Tests/Cache/TransformTreeFixture.cs diff --git a/DynamicData.Tests/Cache/TransformTreeWithRefreshFixture.cs b/src/DynamicData.Tests/Cache/TransformTreeWithRefreshFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TransformTreeWithRefreshFixture.cs rename to src/DynamicData.Tests/Cache/TransformTreeWithRefreshFixture.cs diff --git a/DynamicData.Tests/Cache/TrueForAllFixture.cs b/src/DynamicData.Tests/Cache/TrueForAllFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TrueForAllFixture.cs rename to src/DynamicData.Tests/Cache/TrueForAllFixture.cs diff --git a/DynamicData.Tests/Cache/TrueForAnyFixture.cs b/src/DynamicData.Tests/Cache/TrueForAnyFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/TrueForAnyFixture.cs rename to src/DynamicData.Tests/Cache/TrueForAnyFixture.cs diff --git a/DynamicData.Tests/Cache/WatchFixture.cs b/src/DynamicData.Tests/Cache/WatchFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/WatchFixture.cs rename to src/DynamicData.Tests/Cache/WatchFixture.cs diff --git a/DynamicData.Tests/Cache/WatcherFixture.cs b/src/DynamicData.Tests/Cache/WatcherFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/WatcherFixture.cs rename to src/DynamicData.Tests/Cache/WatcherFixture.cs diff --git a/DynamicData.Tests/Cache/XorFixture.cs b/src/DynamicData.Tests/Cache/XorFixture.cs similarity index 100% rename from DynamicData.Tests/Cache/XorFixture.cs rename to src/DynamicData.Tests/Cache/XorFixture.cs diff --git a/DynamicData.Tests/Domain/Animal.cs b/src/DynamicData.Tests/Domain/Animal.cs similarity index 100% rename from DynamicData.Tests/Domain/Animal.cs rename to src/DynamicData.Tests/Domain/Animal.cs diff --git a/DynamicData.Tests/Domain/ParentAndChildren.cs b/src/DynamicData.Tests/Domain/ParentAndChildren.cs similarity index 100% rename from DynamicData.Tests/Domain/ParentAndChildren.cs rename to src/DynamicData.Tests/Domain/ParentAndChildren.cs diff --git a/DynamicData.Tests/Domain/ParentChild.cs b/src/DynamicData.Tests/Domain/ParentChild.cs similarity index 100% rename from DynamicData.Tests/Domain/ParentChild.cs rename to src/DynamicData.Tests/Domain/ParentChild.cs diff --git a/DynamicData.Tests/Domain/Person.cs b/src/DynamicData.Tests/Domain/Person.cs similarity index 96% rename from DynamicData.Tests/Domain/Person.cs rename to src/DynamicData.Tests/Domain/Person.cs index 2f99b746e..23ed5144a 100644 --- a/DynamicData.Tests/Domain/Person.cs +++ b/src/DynamicData.Tests/Domain/Person.cs @@ -1,118 +1,118 @@ -using System; -using System.Collections.Generic; -using DynamicData.Binding; - -namespace DynamicData.Tests.Domain -{ - public class Person : AbstractNotifyPropertyChanged, IEquatable - { - public string ParentName { get; } - public string Name { get; } - public string Gender { get; } - public string Key => Name; - private int _age; - - public Person(string firstname, string lastname, int age, string gender = "F", string parentName = null) - : this(firstname + " " + lastname, age, gender, parentName) - { - } - - public Person(string name, int age, string gender = "F", string parentName = null) - { - Name = name; - _age = age; - Gender = gender; - ParentName = parentName ?? string.Empty; - } - - public int Age - { - get => _age; - set => SetAndRaise(ref _age, value); - } - - #region Equality Members - - public bool Equals(Person other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return string.Equals(Name, other.Name); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((Person)obj); - } - - public override int GetHashCode() - { - return (Name != null ? Name.GetHashCode() : 0); - } - - public static bool operator ==(Person left, Person right) - { - return Equals(left, right); - } - - public static bool operator !=(Person left, Person right) - { - return !Equals(left, right); - } - - private sealed class AgeEqualityComparer : IEqualityComparer - { - public bool Equals(Person x, Person y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return x._age == y._age; - } - - public int GetHashCode(Person obj) - { - return obj._age; - } - } - - public static IEqualityComparer AgeComparer { get; } = new AgeEqualityComparer(); - - - private sealed class NameAgeGenderEqualityComparer : IEqualityComparer - { - public bool Equals(Person x, Person y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return string.Equals(x.Name, y.Name) && x._age == y._age && string.Equals(x.Gender, y.Gender); - } - - public int GetHashCode(Person obj) - { - unchecked - { - var hashCode = (obj.Name != null ? obj.Name.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ obj._age; - hashCode = (hashCode * 397) ^ (obj.Gender != null ? obj.Gender.GetHashCode() : 0); - return hashCode; - } - } - } - - public static IEqualityComparer NameAgeGenderComparer { get; } = new NameAgeGenderEqualityComparer(); - - #endregion - - public override string ToString() - { - return $"{Name}. {Age}"; - } - } -} +using System; +using System.Collections.Generic; +using DynamicData.Binding; + +namespace DynamicData.Tests.Domain +{ + public class Person : AbstractNotifyPropertyChanged, IEquatable + { + public string ParentName { get; } + public string Name { get; } + public string Gender { get; } + public string Key => Name; + private int _age; + + public Person(string firstname, string lastname, int age, string gender = "F", string parentName = null) + : this(firstname + " " + lastname, age, gender, parentName) + { + } + + public Person(string name, int age, string gender = "F", string parentName = null) + { + Name = name; + _age = age; + Gender = gender; + ParentName = parentName ?? string.Empty; + } + + public int Age + { + get => _age; + set => SetAndRaise(ref _age, value); + } + + #region Equality Members + + public bool Equals(Person other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(Name, other.Name); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Person)obj); + } + + public override int GetHashCode() + { + return (Name != null ? Name.GetHashCode() : 0); + } + + public static bool operator ==(Person left, Person right) + { + return Equals(left, right); + } + + public static bool operator !=(Person left, Person right) + { + return !Equals(left, right); + } + + private sealed class AgeEqualityComparer : IEqualityComparer + { + public bool Equals(Person x, Person y) + { + if (ReferenceEquals(x, y)) return true; + if (ReferenceEquals(x, null)) return false; + if (ReferenceEquals(y, null)) return false; + if (x.GetType() != y.GetType()) return false; + return x._age == y._age; + } + + public int GetHashCode(Person obj) + { + return obj._age; + } + } + + public static IEqualityComparer AgeComparer { get; } = new AgeEqualityComparer(); + + + private sealed class NameAgeGenderEqualityComparer : IEqualityComparer + { + public bool Equals(Person x, Person y) + { + if (ReferenceEquals(x, y)) return true; + if (ReferenceEquals(x, null)) return false; + if (ReferenceEquals(y, null)) return false; + if (x.GetType() != y.GetType()) return false; + return string.Equals(x.Name, y.Name) && x._age == y._age && string.Equals(x.Gender, y.Gender); + } + + public int GetHashCode(Person obj) + { + unchecked + { + var hashCode = (obj.Name != null ? obj.Name.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ obj._age; + hashCode = (hashCode * 397) ^ (obj.Gender != null ? obj.Gender.GetHashCode() : 0); + return hashCode; + } + } + } + + public static IEqualityComparer NameAgeGenderComparer { get; } = new NameAgeGenderEqualityComparer(); + + #endregion + + public override string ToString() + { + return $"{Name}. {Age}"; + } + } +} diff --git a/DynamicData.Tests/Domain/PersonEmployment.cs b/src/DynamicData.Tests/Domain/PersonEmployment.cs similarity index 100% rename from DynamicData.Tests/Domain/PersonEmployment.cs rename to src/DynamicData.Tests/Domain/PersonEmployment.cs diff --git a/DynamicData.Tests/Domain/PersonObs.cs b/src/DynamicData.Tests/Domain/PersonObs.cs similarity index 97% rename from DynamicData.Tests/Domain/PersonObs.cs rename to src/DynamicData.Tests/Domain/PersonObs.cs index f81b3b773..2f3529269 100644 --- a/DynamicData.Tests/Domain/PersonObs.cs +++ b/src/DynamicData.Tests/Domain/PersonObs.cs @@ -1,121 +1,121 @@ -using System; -using System.Collections.Generic; -using System.Reactive.Subjects; -using DynamicData.Annotations; - -namespace DynamicData.Tests.Domain -{ - public class PersonObs : IEquatable - { - public string ParentName { get; } - public string Name { get; } - public string Gender { get; } - public string Key => Name; - [NotNull] - private readonly BehaviorSubject _age; - - public PersonObs(string firstname, string lastname, int age, string gender = "F", string parentName = null) - : this(firstname + " " + lastname, age, gender, parentName) - { - } - - public PersonObs(string name, int age, string gender = "F", string parentName = null) - { - Name = name; - _age = new BehaviorSubject(age); - Gender = gender; - ParentName = parentName ?? string.Empty; - } - - public IObservable Age => _age; - - public void SetAge(int age) - { - _age.OnNext(age); - } - - #region Equality Members - - public bool Equals(PersonObs other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return string.Equals(Name, other.Name); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((PersonObs)obj); - } - - public override int GetHashCode() - { - return (Name != null ? Name.GetHashCode() : 0); - } - - public static bool operator ==(PersonObs left, PersonObs right) - { - return Equals(left, right); - } - - public static bool operator !=(PersonObs left, PersonObs right) - { - return !Equals(left, right); - } - - private sealed class AgeEqualityComparer : IEqualityComparer - { - public bool Equals(PersonObs x, PersonObs y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return x._age.Value == y._age.Value; - } - - public int GetHashCode(PersonObs obj) - { - return obj._age.Value; - } - } - - public static IEqualityComparer AgeComparer { get; } = new AgeEqualityComparer(); - - - private sealed class NameAgeGenderEqualityComparer : IEqualityComparer - { - public bool Equals(PersonObs x, PersonObs y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return string.Equals(x.Name, y.Name) && x._age == y._age && string.Equals(x.Gender, y.Gender); - } - - public int GetHashCode(PersonObs obj) - { - unchecked - { - var hashCode = (obj.Name != null ? obj.Name.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ obj._age.Value; - hashCode = (hashCode * 397) ^ (obj.Gender != null ? obj.Gender.GetHashCode() : 0); - return hashCode; - } - } - } - - public static IEqualityComparer NameAgeGenderComparer { get; } = new NameAgeGenderEqualityComparer(); - - #endregion - - public override string ToString() - { - return $"{Name}. {_age.Value}"; - } - } -} +using System; +using System.Collections.Generic; +using System.Reactive.Subjects; +using DynamicData.Annotations; + +namespace DynamicData.Tests.Domain +{ + public class PersonObs : IEquatable + { + public string ParentName { get; } + public string Name { get; } + public string Gender { get; } + public string Key => Name; + [NotNull] + private readonly BehaviorSubject _age; + + public PersonObs(string firstname, string lastname, int age, string gender = "F", string parentName = null) + : this(firstname + " " + lastname, age, gender, parentName) + { + } + + public PersonObs(string name, int age, string gender = "F", string parentName = null) + { + Name = name; + _age = new BehaviorSubject(age); + Gender = gender; + ParentName = parentName ?? string.Empty; + } + + public IObservable Age => _age; + + public void SetAge(int age) + { + _age.OnNext(age); + } + + #region Equality Members + + public bool Equals(PersonObs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(Name, other.Name); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((PersonObs)obj); + } + + public override int GetHashCode() + { + return (Name != null ? Name.GetHashCode() : 0); + } + + public static bool operator ==(PersonObs left, PersonObs right) + { + return Equals(left, right); + } + + public static bool operator !=(PersonObs left, PersonObs right) + { + return !Equals(left, right); + } + + private sealed class AgeEqualityComparer : IEqualityComparer + { + public bool Equals(PersonObs x, PersonObs y) + { + if (ReferenceEquals(x, y)) return true; + if (ReferenceEquals(x, null)) return false; + if (ReferenceEquals(y, null)) return false; + if (x.GetType() != y.GetType()) return false; + return x._age.Value == y._age.Value; + } + + public int GetHashCode(PersonObs obj) + { + return obj._age.Value; + } + } + + public static IEqualityComparer AgeComparer { get; } = new AgeEqualityComparer(); + + + private sealed class NameAgeGenderEqualityComparer : IEqualityComparer + { + public bool Equals(PersonObs x, PersonObs y) + { + if (ReferenceEquals(x, y)) return true; + if (ReferenceEquals(x, null)) return false; + if (ReferenceEquals(y, null)) return false; + if (x.GetType() != y.GetType()) return false; + return string.Equals(x.Name, y.Name) && x._age == y._age && string.Equals(x.Gender, y.Gender); + } + + public int GetHashCode(PersonObs obj) + { + unchecked + { + var hashCode = (obj.Name != null ? obj.Name.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ obj._age.Value; + hashCode = (hashCode * 397) ^ (obj.Gender != null ? obj.Gender.GetHashCode() : 0); + return hashCode; + } + } + } + + public static IEqualityComparer NameAgeGenderComparer { get; } = new NameAgeGenderEqualityComparer(); + + #endregion + + public override string ToString() + { + return $"{Name}. {_age.Value}"; + } + } +} diff --git a/DynamicData.Tests/Domain/PersonWithChildren.cs b/src/DynamicData.Tests/Domain/PersonWithChildren.cs similarity index 100% rename from DynamicData.Tests/Domain/PersonWithChildren.cs rename to src/DynamicData.Tests/Domain/PersonWithChildren.cs diff --git a/DynamicData.Tests/Domain/PersonWithEmployment.cs b/src/DynamicData.Tests/Domain/PersonWithEmployment.cs similarity index 100% rename from DynamicData.Tests/Domain/PersonWithEmployment.cs rename to src/DynamicData.Tests/Domain/PersonWithEmployment.cs diff --git a/DynamicData.Tests/Domain/PersonWithFriends.cs b/src/DynamicData.Tests/Domain/PersonWithFriends.cs similarity index 100% rename from DynamicData.Tests/Domain/PersonWithFriends.cs rename to src/DynamicData.Tests/Domain/PersonWithFriends.cs diff --git a/DynamicData.Tests/Domain/PersonWithGender.cs b/src/DynamicData.Tests/Domain/PersonWithGender.cs similarity index 100% rename from DynamicData.Tests/Domain/PersonWithGender.cs rename to src/DynamicData.Tests/Domain/PersonWithGender.cs diff --git a/DynamicData.Tests/Domain/PersonWithRelations.cs b/src/DynamicData.Tests/Domain/PersonWithRelations.cs similarity index 100% rename from DynamicData.Tests/Domain/PersonWithRelations.cs rename to src/DynamicData.Tests/Domain/PersonWithRelations.cs diff --git a/DynamicData.Tests/Domain/Pet.cs b/src/DynamicData.Tests/Domain/Pet.cs similarity index 100% rename from DynamicData.Tests/Domain/Pet.cs rename to src/DynamicData.Tests/Domain/Pet.cs diff --git a/DynamicData.Tests/Domain/RandomPersonGenerator.cs b/src/DynamicData.Tests/Domain/RandomPersonGenerator.cs similarity index 100% rename from DynamicData.Tests/Domain/RandomPersonGenerator.cs rename to src/DynamicData.Tests/Domain/RandomPersonGenerator.cs diff --git a/DynamicData.Tests/Domain/SelfObservingPerson.cs b/src/DynamicData.Tests/Domain/SelfObservingPerson.cs similarity index 100% rename from DynamicData.Tests/Domain/SelfObservingPerson.cs rename to src/DynamicData.Tests/Domain/SelfObservingPerson.cs diff --git a/DynamicData.Tests/DynamicData.Tests.csproj b/src/DynamicData.Tests/DynamicData.Tests.csproj similarity index 97% rename from DynamicData.Tests/DynamicData.Tests.csproj rename to src/DynamicData.Tests/DynamicData.Tests.csproj index a0c0322eb..9c4064a82 100644 --- a/DynamicData.Tests/DynamicData.Tests.csproj +++ b/src/DynamicData.Tests/DynamicData.Tests.csproj @@ -1,23 +1,23 @@ - - - netcoreapp2.2;net462 - $(NoWarn);CS0618 - - - - - - - - - - - - - - - - - - - + + + netcoreapp2.2;net462 + $(NoWarn);CS0618 + + + + + + + + + + + + + + + + + + + diff --git a/DynamicData.Tests/EnumerableExFixtures.cs b/src/DynamicData.Tests/EnumerableExFixtures.cs similarity index 100% rename from DynamicData.Tests/EnumerableExFixtures.cs rename to src/DynamicData.Tests/EnumerableExFixtures.cs diff --git a/DynamicData.Tests/Kernal/CacheUpdaterFixture.cs b/src/DynamicData.Tests/Kernal/CacheUpdaterFixture.cs similarity index 100% rename from DynamicData.Tests/Kernal/CacheUpdaterFixture.cs rename to src/DynamicData.Tests/Kernal/CacheUpdaterFixture.cs diff --git a/DynamicData.Tests/Kernal/DistinctUpdateFixture.cs b/src/DynamicData.Tests/Kernal/DistinctUpdateFixture.cs similarity index 100% rename from DynamicData.Tests/Kernal/DistinctUpdateFixture.cs rename to src/DynamicData.Tests/Kernal/DistinctUpdateFixture.cs diff --git a/DynamicData.Tests/Kernal/EnumerableEx.cs b/src/DynamicData.Tests/Kernal/EnumerableEx.cs similarity index 100% rename from DynamicData.Tests/Kernal/EnumerableEx.cs rename to src/DynamicData.Tests/Kernal/EnumerableEx.cs diff --git a/DynamicData.Tests/Kernal/KeyValueFixture.cs b/src/DynamicData.Tests/Kernal/KeyValueFixture.cs similarity index 100% rename from DynamicData.Tests/Kernal/KeyValueFixture.cs rename to src/DynamicData.Tests/Kernal/KeyValueFixture.cs diff --git a/DynamicData.Tests/Kernal/OptionFixture.cs b/src/DynamicData.Tests/Kernal/OptionFixture.cs similarity index 100% rename from DynamicData.Tests/Kernal/OptionFixture.cs rename to src/DynamicData.Tests/Kernal/OptionFixture.cs diff --git a/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs b/src/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs similarity index 97% rename from DynamicData.Tests/Kernal/SourceUpdaterFixture.cs rename to src/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs index 0906ebf2f..7017690ed 100644 --- a/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs +++ b/src/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs @@ -1,143 +1,143 @@ -using System.Linq; -using DynamicData.Cache.Internal; -using DynamicData.Tests.Domain; -using FluentAssertions; -using Xunit; - -namespace DynamicData.Tests.Kernal -{ - public class SourceUpdaterFixture - { - private readonly ChangeAwareCache _cache; - private readonly CacheUpdater _updater; - - public SourceUpdaterFixture() - { - _cache = new ChangeAwareCache(); - _updater = new CacheUpdater(_cache, p => p.Name); - } - - [Fact] - public void Add() - { - var person = new Person("Adult1", 50); - _updater.AddOrUpdate(person); - IChangeSet updates = _cache.CaptureChanges(); - - _cache.Lookup("Adult1").Value.Should().Be(person); - _cache.Count.Should().Be(1); - 1.Should().Be(updates.Count, "Should be 1 updates"); - updates.First().Should().Be(new Change(ChangeReason.Add, person.Name, person), "Should be 1 updates"); - } - - [Fact] - public void AttemptedRemovalOfANonExistentKeyWillBeIgnored() - { - const string key = "Adult1"; - - _updater.Remove(key); - IChangeSet updates = _cache.CaptureChanges(); - - _cache.Count.Should().Be(0); - updates.Count.Should().Be(0, "Should be 0 updates"); - } - - [Fact] - public void BatchOfUniqueUpdates() - { - Person[] people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - _updater.AddOrUpdate(people); - var updates = _cache.CaptureChanges(); - - - _cache.Items.ToArray().ShouldAllBeEquivalentTo(people); - _cache.Count.Should().Be(100); - updates.Adds.Should().Be(100); - updates.Count.Should().Be(100); - } - - [Fact] - public void BatchRemoves() - { - Person[] people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - _updater.AddOrUpdate(people); - _updater.Remove(people); - IChangeSet updates = _cache.CaptureChanges(); - - _cache.Count.Should().Be(0, "Everything should be removed"); - 100.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 100 adds"); - 100.Should().Be(updates.Count(update => update.Reason == ChangeReason.Remove), "Should be 100 removes"); - 200.Should().Be(updates.Count, "Should be 200 updates"); - } - - [Fact] - public void BatchSuccessiveUpdates() - { - Person[] people = Enumerable.Range(1, 100).Select(i => new Person("Name1", i)).ToArray(); - _updater.AddOrUpdate(people); - - IChangeSet updates = _cache.CaptureChanges(); - - _cache.Lookup("Name1").Value.Age.Should().Be(100); - _cache.Count.Should().Be(1, "Successive updates should replace cache value"); - 99.Should().Be(updates.Count(update => update.Reason == ChangeReason.Update), "Should be 99 updates"); - 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 1 add"); - 100.Should().Be(updates.Count, "Should be 100 updates"); - } - - [Fact] - public void CanRemove() - { - const string key = "Adult1"; - - var person = new Person(key, 50); - _updater.AddOrUpdate(person); - _updater.Remove(person); - IChangeSet updates = _cache.CaptureChanges(); - - _cache.Count.Should().Be(0); - 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 1 add"); - 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Remove), "Should be 1 remove"); - 2.Should().Be(updates.Count, "Should be 2 updates"); - } - - [Fact] - public void CanUpdate() - { - const string key = "Adult1"; - - var newperson = new Person(key, 50); - var updated = new Person(key, 51); - _updater.AddOrUpdate(newperson); - _updater.AddOrUpdate(updated); - IChangeSet updates = _cache.CaptureChanges(); - - _cache.Lookup(key).Value.Should().Be(updated); - _cache.Count.Should().Be(1); - 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 1 adds"); - 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Update), "Should be 1 update"); - 2.Should().Be(updates.Count, "Should be 2 updates"); - } - - [Fact] - public void Clear() - { - Person[] people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - _updater.AddOrUpdate(people); - _updater.Clear(); - IChangeSet updates = _cache.CaptureChanges(); - - _cache.Count.Should().Be(0, "Everything should be removed"); - 100.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 100 adds"); - 100.Should().Be(updates.Count(update => update.Reason == ChangeReason.Remove), "Should be 100 removes"); - 200.Should().Be(updates.Count, "Should be 200 updates"); - } - - [Fact] - public void NullSelectorWillThrow() - { - // Assert.Throws(() => new SourceUpdater(_cache, new KeySelector(null))); - } - - } -} +using System.Linq; +using DynamicData.Cache.Internal; +using DynamicData.Tests.Domain; +using FluentAssertions; +using Xunit; + +namespace DynamicData.Tests.Kernal +{ + public class SourceUpdaterFixture + { + private readonly ChangeAwareCache _cache; + private readonly CacheUpdater _updater; + + public SourceUpdaterFixture() + { + _cache = new ChangeAwareCache(); + _updater = new CacheUpdater(_cache, p => p.Name); + } + + [Fact] + public void Add() + { + var person = new Person("Adult1", 50); + _updater.AddOrUpdate(person); + IChangeSet updates = _cache.CaptureChanges(); + + _cache.Lookup("Adult1").Value.Should().Be(person); + _cache.Count.Should().Be(1); + 1.Should().Be(updates.Count, "Should be 1 updates"); + updates.First().Should().Be(new Change(ChangeReason.Add, person.Name, person), "Should be 1 updates"); + } + + [Fact] + public void AttemptedRemovalOfANonExistentKeyWillBeIgnored() + { + const string key = "Adult1"; + + _updater.Remove(key); + IChangeSet updates = _cache.CaptureChanges(); + + _cache.Count.Should().Be(0); + updates.Count.Should().Be(0, "Should be 0 updates"); + } + + [Fact] + public void BatchOfUniqueUpdates() + { + Person[] people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); + _updater.AddOrUpdate(people); + var updates = _cache.CaptureChanges(); + + + _cache.Items.ToArray().ShouldAllBeEquivalentTo(people); + _cache.Count.Should().Be(100); + updates.Adds.Should().Be(100); + updates.Count.Should().Be(100); + } + + [Fact] + public void BatchRemoves() + { + Person[] people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); + _updater.AddOrUpdate(people); + _updater.Remove(people); + IChangeSet updates = _cache.CaptureChanges(); + + _cache.Count.Should().Be(0, "Everything should be removed"); + 100.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 100 adds"); + 100.Should().Be(updates.Count(update => update.Reason == ChangeReason.Remove), "Should be 100 removes"); + 200.Should().Be(updates.Count, "Should be 200 updates"); + } + + [Fact] + public void BatchSuccessiveUpdates() + { + Person[] people = Enumerable.Range(1, 100).Select(i => new Person("Name1", i)).ToArray(); + _updater.AddOrUpdate(people); + + IChangeSet updates = _cache.CaptureChanges(); + + _cache.Lookup("Name1").Value.Age.Should().Be(100); + _cache.Count.Should().Be(1, "Successive updates should replace cache value"); + 99.Should().Be(updates.Count(update => update.Reason == ChangeReason.Update), "Should be 99 updates"); + 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 1 add"); + 100.Should().Be(updates.Count, "Should be 100 updates"); + } + + [Fact] + public void CanRemove() + { + const string key = "Adult1"; + + var person = new Person(key, 50); + _updater.AddOrUpdate(person); + _updater.Remove(person); + IChangeSet updates = _cache.CaptureChanges(); + + _cache.Count.Should().Be(0); + 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 1 add"); + 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Remove), "Should be 1 remove"); + 2.Should().Be(updates.Count, "Should be 2 updates"); + } + + [Fact] + public void CanUpdate() + { + const string key = "Adult1"; + + var newperson = new Person(key, 50); + var updated = new Person(key, 51); + _updater.AddOrUpdate(newperson); + _updater.AddOrUpdate(updated); + IChangeSet updates = _cache.CaptureChanges(); + + _cache.Lookup(key).Value.Should().Be(updated); + _cache.Count.Should().Be(1); + 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 1 adds"); + 1.Should().Be(updates.Count(update => update.Reason == ChangeReason.Update), "Should be 1 update"); + 2.Should().Be(updates.Count, "Should be 2 updates"); + } + + [Fact] + public void Clear() + { + Person[] people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); + _updater.AddOrUpdate(people); + _updater.Clear(); + IChangeSet updates = _cache.CaptureChanges(); + + _cache.Count.Should().Be(0, "Everything should be removed"); + 100.Should().Be(updates.Count(update => update.Reason == ChangeReason.Add), "Should be 100 adds"); + 100.Should().Be(updates.Count(update => update.Reason == ChangeReason.Remove), "Should be 100 removes"); + 200.Should().Be(updates.Count, "Should be 200 updates"); + } + + [Fact] + public void NullSelectorWillThrow() + { + // Assert.Throws(() => new SourceUpdater(_cache, new KeySelector(null))); + } + + } +} diff --git a/DynamicData.Tests/Kernal/UpdateFixture.cs b/src/DynamicData.Tests/Kernal/UpdateFixture.cs similarity index 100% rename from DynamicData.Tests/Kernal/UpdateFixture.cs rename to src/DynamicData.Tests/Kernal/UpdateFixture.cs diff --git a/DynamicData.Tests/List/AndFixture.cs b/src/DynamicData.Tests/List/AndFixture.cs similarity index 100% rename from DynamicData.Tests/List/AndFixture.cs rename to src/DynamicData.Tests/List/AndFixture.cs diff --git a/DynamicData.Tests/List/AutoRefreshFixture.cs b/src/DynamicData.Tests/List/AutoRefreshFixture.cs similarity index 97% rename from DynamicData.Tests/List/AutoRefreshFixture.cs rename to src/DynamicData.Tests/List/AutoRefreshFixture.cs index eb278ef84..98a440229 100644 --- a/DynamicData.Tests/List/AutoRefreshFixture.cs +++ b/src/DynamicData.Tests/List/AutoRefreshFixture.cs @@ -1,479 +1,479 @@ -using System; -using System.Linq; -using DynamicData.Binding; -using DynamicData.Kernel; -using DynamicData.Tests.Domain; -using FluentAssertions; -using Microsoft.Reactive.Testing; -using Xunit; - -namespace DynamicData.Tests.List -{ - - public class AutoRefreshFixture - { - [Fact] - public void AutoRefresh() - { - var items = Enumerable.Range(1, 100) - .Select(i => new Person("Person" + i, 1)) - .ToArray(); - - //result should only be true when all items are set to true - using (var list = new SourceList()) - using (var results = list.Connect().AutoRefresh(p=>p.Age).AsAggregator()) - { - list.AddRange(items); - - results.Data.Count.Should().Be(100); - results.Messages.Count.Should().Be(1); - - items[0].Age = 10; - results.Data.Count.Should().Be(100); - results.Messages.Count.Should().Be(2); - - results.Messages[1].First().Reason.Should().Be(ListChangeReason.Refresh); - - //remove an item and check no change is fired - var toRemove = items[1]; - list.Remove(toRemove); - results.Data.Count.Should().Be(99); - results.Messages.Count.Should().Be(3); - toRemove.Age = 100; - results.Messages.Count.Should().Be(3); - - //add it back in and check it updates - list.Add(toRemove); - results.Messages.Count.Should().Be(4); - toRemove.Age = 101; - results.Messages.Count.Should().Be(5); - - results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Refresh); - } - } - - [Fact] - public void AutoRefreshBatched() - { - var scheduler = new TestScheduler(); - - var items = Enumerable.Range(1, 100) - .Select(i => new Person("Person" + i, 1)) - .ToArray(); - - //result should only be true when all items are set to true - using (var list = new SourceList()) - using (var results = list.Connect().AutoRefresh(p=>p.Age, TimeSpan.FromSeconds(1),scheduler: scheduler).AsAggregator()) - { - list.AddRange(items); - - results.Data.Count.Should().Be(100); - results.Messages.Count.Should().Be(1); - - //update 50 records - items.Skip(50) - .ForEach(p => p.Age = p.Age + 1); - - scheduler.AdvanceBy(TimeSpan.FromSeconds(1).Ticks); - - //should be another message with 50 refreshes - results.Messages.Count.Should().Be(2); - results.Messages[1].Refreshes.Should().Be(50); - } - } - - [Fact] - public void AutoRefreshFilter() - { - var items = Enumerable.Range(1, 100) - .Select(i => new Person("Person" + i, i)) - .ToArray(); - - //result should only be true when all items are set to true - using (var list = new SourceList()) - using (var results = list.Connect().AutoRefresh(p=>p.Age).Filter(p=>p.Age>50).AsAggregator()) - { - list.AddRange(items); - - results.Data.Count.Should().Be(50); - results.Messages.Count.Should().Be(1); - - //update an item which did not match the filter and does so after change - items[0].Age = 60; - results.Data.Count.Should().Be(51); - results.Messages.Count.Should().Be(2); - results.Messages[1].First().Reason.Should().Be(ListChangeReason.Add); - - //check for removes - items[0].Age = 21; - results.Data.Count.Should().Be(50); - results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Remove); - items[0].Age = 60; - - //update an item which matched the filter and still does [refresh should have propagated] - items[60].Age = 160; - results.Data.Count.Should().Be(51); - results.Messages.Count.Should().Be(5); - results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Replace); - - //remove an item and check no change is fired - var toRemove = items[65]; - list.Remove(toRemove); - results.Data.Count.Should().Be(50); - results.Messages.Count.Should().Be(6); - toRemove.Age = 100; - results.Messages.Count.Should().Be(6); - - //add it back in and check it updates - list.Add(toRemove); - results.Messages.Count.Should().Be(7); - toRemove.Age = 101; - results.Messages.Count.Should().Be(8); - - - - results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Replace); - } - } - - [Fact] - public void AutoRefreshTransform() - { - var items = Enumerable.Range(1, 100) - .Select(i => new Person("Person" + i, i)) - .ToArray(); - - //result should only be true when all items are set to true - using (var list = new SourceList()) - using (var results = list.Connect() - .AutoRefresh(p=>p.Age) - .Transform((p,idx) => new TransformedPerson(p,idx)) - .AsAggregator()) - { - list.AddRange(items); - - results.Data.Count.Should().Be(100); - results.Messages.Count.Should().Be(1); - - //update an item which did not match the filter and does so after change - items[0].Age = 60; - results.Messages.Count.Should().Be(2); - results.Messages.Last().Refreshes.Should().Be(1); - results.Messages.Last().First().Item.Reason.Should().Be(ListChangeReason.Refresh); - results.Messages.Last().First().Item.Current.Index.Should().Be(0); - - items[60].Age = 160; - results.Messages.Count.Should().Be(3); - results.Messages.Last().Refreshes.Should().Be(1); - results.Messages.Last().First().Item.Reason.Should().Be(ListChangeReason.Refresh); - results.Messages.Last().First().Item.Current.Index.Should().Be(60); - } - } - - [Fact] - public void AutoRefreshSort() - { - var items = Enumerable.Range(1, 100) - .Select(i => new Person("Person" + i, i)) - .OrderByDescending(p=>p.Age) - .ToArray(); - - var comparer = SortExpressionComparer.Ascending(p => p.Age); - - //result should only be true when all items are set to true - using (var list = new SourceList()) - using (var results = list.Connect() - .AutoRefresh(p => p.Age) - .Sort(SortExpressionComparer.Ascending(p=>p.Age)) - .AsAggregator()) - { - - void CheckOrder() - { - var sorted = items.OrderBy(p => p, comparer).ToArray(); - results.Data.Items.ShouldAllBeEquivalentTo(sorted); - } - - list.AddRange(items); - - results.Data.Count.Should().Be(100); - results.Messages.Count.Should().Be(1); - CheckOrder(); - - items[0].Age = 60; - CheckOrder(); - results.Messages.Count.Should().Be(2); - results.Messages.Last().Refreshes.Should().Be(1); - results.Messages.Last().Moves.Should().Be(1); - - items[90].Age = -1; //move to beginning - CheckOrder(); - results.Messages.Count.Should().Be(3); - results.Messages.Last().Refreshes.Should().Be(1); - results.Messages.Last().Moves.Should().Be(1); - - items[50].Age = 49; //same position so no move - CheckOrder(); - results.Messages.Count.Should().Be(4); - results.Messages.Last().Refreshes.Should().Be(1); - results.Messages.Last().Moves.Should().Be(0); - - items[50].Age = 51; //same position so no move - CheckOrder(); - results.Messages.Count.Should().Be(5); - results.Messages.Last().Refreshes.Should().Be(1); - results.Messages.Last().Moves.Should().Be(1); - } - } - - [Fact] - public void AutoRefreshGroup() - { - var items = Enumerable.Range(1, 100) - .Select(i => new Person("Person" + i, i)) - .ToArray(); - - //result should only be true when all items are set to true - using (var list = new SourceList()) - using (var results = list.Connect() - .AutoRefresh(p => p.Age) - .GroupOn(p=>p.Age % 10) - .AsAggregator()) - { - void CheckContent() - { - foreach (var grouping in items.GroupBy(p => p.Age % 10)) - { - var childGroup = results.Data.Items.Single(g => g.GroupKey == grouping.Key); - var expected = grouping.OrderBy(p => p.Name); - var actual = childGroup.List.Items.OrderBy(p => p.Name); - actual.ShouldAllBeEquivalentTo(expected); - } - } - - list.AddRange(items); - results.Data.Count.Should().Be(10); - results.Messages.Count.Should().Be(1); - CheckContent(); - - //move person from group 1 to 2 - items[0].Age = items[0].Age + 1; - CheckContent(); - - //change the value and move to a grouping which does not yet exist - items[1].Age = -1; - results.Data.Count.Should().Be(11); - results.Data.Items.Last().GroupKey.Should().Be(-1); - results.Data.Items.Last().List.Count.Should().Be(1); - results.Data.Items.First().List.Count.Should().Be(9); - CheckContent(); - - //put the value back where it was and check the group was removed - items[1].Age = 1; - results.Data.Count.Should().Be(10); - CheckContent(); - - - var groupOf3 = results.Data.Items.ElementAt(2); - - IChangeSet changes = null; - groupOf3.List.Connect().Subscribe(c => changes = c); - - //refresh an item which makes it belong to the same group - should then propagate a refresh - items[2].Age = 13; - changes.Should().NotBeNull(); - changes.Count.Should().Be(1); - changes.First().Reason.Should().Be(ListChangeReason.Replace); - changes.First().Item.Current.Should().BeSameAs(items[2]); - } - } - - [Fact] - public void AutoRefreshGroupImmutable() - { - var items = Enumerable.Range(1, 100) - .Select(i => new Person("Person" + i, i)) - .ToArray(); - - //result should only be true when all items are set to true - using (var list = new SourceList()) - using (var results = list.Connect() - .AutoRefresh(p => p.Age) - .GroupWithImmutableState(p => p.Age % 10) - .AsAggregator()) - { - void CheckContent() - { - foreach (var grouping in items.GroupBy(p => p.Age % 10)) - { - var childGroup = results.Data.Items.Single(g => g.Key == grouping.Key); - var expected = grouping.OrderBy(p => p.Name); - var actual = childGroup.Items.OrderBy(p => p.Name); - actual.ShouldAllBeEquivalentTo(expected); - } - } - - list.AddRange(items); - results.Data.Count.Should().Be(10); - results.Messages.Count.Should().Be(1); - CheckContent(); - - //move person from group 1 to 2 - items[0].Age = items[0].Age + 1; - CheckContent(); - - //change the value and move to a grouping which does not yet exist - items[1].Age = -1; - results.Data.Count.Should().Be(11); - results.Data.Items.Last().Key.Should().Be(-1); - results.Data.Items.Last().Count.Should().Be(1); - results.Data.Items.First().Count.Should().Be(9); - CheckContent(); - - //put the value back where it was and check the group was removed - items[1].Age = 1; - results.Data.Count.Should().Be(10); - results.Messages.Count.Should().Be(4); - CheckContent(); - - //refresh an item which makes it belong to the same group - should then propagate a refresh - items[2].Age = 13; - CheckContent(); - - results.Messages.Count.Should().Be(5); - } - } - - - [Fact] - public void AutoRefreshDistinct() - { - var items = Enumerable.Range(1, 100) - .Select(i => new Person("Person" + i, i)) - .ToArray(); - - //result should only be true when all items are set to true - using (var list = new SourceList()) - using (var results = list.Connect() - .AutoRefresh(p => p.Age) - .DistinctValues(p => p.Age / 10) - .AsAggregator()) - { - list.AddRange(items); - - results.Data.Count.Should().Be(11); - results.Messages.Count.Should().Be(1); - - //update an item which did not match the filter and does so after change - items[50].Age = 500; - results.Data.Count.Should().Be(12); - - results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Add); - results.Messages.Last().First().Item.Current.Should().Be(50); - } - } - - private class TransformedPerson - { - public Person Person { get; } - public int Index { get; } - public DateTime TimeStamp { get; } = DateTime.Now; - - public TransformedPerson(Person person, int index) - { - Person = person; - Index = index; - } - } - - [Fact] - public void AutoRefreshSelected() - { - //test added as v6 broke unit test in DynamicData.Snippets - var initialItems = Enumerable.Range(1, 10) - .Select(i => new SelectableItem(i)) - .ToArray(); - - //result should only be true when all items are set to true - using (var sourceList = new SourceList()) - using (var sut = sourceList.Connect().AutoRefresh().Filter(si => si.IsSelected).AsObservableList()) - { - sourceList.AddRange(initialItems); - sut.Count.Should().Be(0); - - initialItems[0].IsSelected = true; - sut.Count.Should().Be(1); - - initialItems[1].IsSelected = true; - sut.Count.Should().Be(2); - - //remove the selected items - sourceList.RemoveRange(0, 2); - sut.Count.Should().Be(0); - } - } - - private class SelectableItem : AbstractNotifyPropertyChanged - { - public int Id { get; } - - public SelectableItem(int id) - { - Id = id; - } - - private bool _isSelected; - - public bool IsSelected - { - get => _isSelected; - set => SetAndRaise(ref _isSelected, value); - } - - protected bool Equals(SelectableItem other) - { - return Id == other.Id; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((SelectableItem)obj); - } - - public override int GetHashCode() - { - return Id; - } - } - - [Fact] - public void RefreshTransformAsList() - { - SourceList list = new SourceList(); - var valueList = list.Connect() - .AutoRefresh(e => e.Value) - .Transform(e => e.Value, true) - .AsObservableList(); - - var obj = new Example { Value = 0 }; - list.Add(obj); - obj.Value = 1; - valueList.Items.First().Should().Be(1); - } - - private class Example : AbstractNotifyPropertyChanged - { - private int _value; - - public int Value - { - get => _value; - set => SetAndRaise(ref _value, value); - } - } - - } -} +using System; +using System.Linq; +using DynamicData.Binding; +using DynamicData.Kernel; +using DynamicData.Tests.Domain; +using FluentAssertions; +using Microsoft.Reactive.Testing; +using Xunit; + +namespace DynamicData.Tests.List +{ + + public class AutoRefreshFixture + { + [Fact] + public void AutoRefresh() + { + var items = Enumerable.Range(1, 100) + .Select(i => new Person("Person" + i, 1)) + .ToArray(); + + //result should only be true when all items are set to true + using (var list = new SourceList()) + using (var results = list.Connect().AutoRefresh(p=>p.Age).AsAggregator()) + { + list.AddRange(items); + + results.Data.Count.Should().Be(100); + results.Messages.Count.Should().Be(1); + + items[0].Age = 10; + results.Data.Count.Should().Be(100); + results.Messages.Count.Should().Be(2); + + results.Messages[1].First().Reason.Should().Be(ListChangeReason.Refresh); + + //remove an item and check no change is fired + var toRemove = items[1]; + list.Remove(toRemove); + results.Data.Count.Should().Be(99); + results.Messages.Count.Should().Be(3); + toRemove.Age = 100; + results.Messages.Count.Should().Be(3); + + //add it back in and check it updates + list.Add(toRemove); + results.Messages.Count.Should().Be(4); + toRemove.Age = 101; + results.Messages.Count.Should().Be(5); + + results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Refresh); + } + } + + [Fact] + public void AutoRefreshBatched() + { + var scheduler = new TestScheduler(); + + var items = Enumerable.Range(1, 100) + .Select(i => new Person("Person" + i, 1)) + .ToArray(); + + //result should only be true when all items are set to true + using (var list = new SourceList()) + using (var results = list.Connect().AutoRefresh(p=>p.Age, TimeSpan.FromSeconds(1),scheduler: scheduler).AsAggregator()) + { + list.AddRange(items); + + results.Data.Count.Should().Be(100); + results.Messages.Count.Should().Be(1); + + //update 50 records + items.Skip(50) + .ForEach(p => p.Age = p.Age + 1); + + scheduler.AdvanceBy(TimeSpan.FromSeconds(1).Ticks); + + //should be another message with 50 refreshes + results.Messages.Count.Should().Be(2); + results.Messages[1].Refreshes.Should().Be(50); + } + } + + [Fact] + public void AutoRefreshFilter() + { + var items = Enumerable.Range(1, 100) + .Select(i => new Person("Person" + i, i)) + .ToArray(); + + //result should only be true when all items are set to true + using (var list = new SourceList()) + using (var results = list.Connect().AutoRefresh(p=>p.Age).Filter(p=>p.Age>50).AsAggregator()) + { + list.AddRange(items); + + results.Data.Count.Should().Be(50); + results.Messages.Count.Should().Be(1); + + //update an item which did not match the filter and does so after change + items[0].Age = 60; + results.Data.Count.Should().Be(51); + results.Messages.Count.Should().Be(2); + results.Messages[1].First().Reason.Should().Be(ListChangeReason.Add); + + //check for removes + items[0].Age = 21; + results.Data.Count.Should().Be(50); + results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Remove); + items[0].Age = 60; + + //update an item which matched the filter and still does [refresh should have propagated] + items[60].Age = 160; + results.Data.Count.Should().Be(51); + results.Messages.Count.Should().Be(5); + results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Replace); + + //remove an item and check no change is fired + var toRemove = items[65]; + list.Remove(toRemove); + results.Data.Count.Should().Be(50); + results.Messages.Count.Should().Be(6); + toRemove.Age = 100; + results.Messages.Count.Should().Be(6); + + //add it back in and check it updates + list.Add(toRemove); + results.Messages.Count.Should().Be(7); + toRemove.Age = 101; + results.Messages.Count.Should().Be(8); + + + + results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Replace); + } + } + + [Fact] + public void AutoRefreshTransform() + { + var items = Enumerable.Range(1, 100) + .Select(i => new Person("Person" + i, i)) + .ToArray(); + + //result should only be true when all items are set to true + using (var list = new SourceList()) + using (var results = list.Connect() + .AutoRefresh(p=>p.Age) + .Transform((p,idx) => new TransformedPerson(p,idx)) + .AsAggregator()) + { + list.AddRange(items); + + results.Data.Count.Should().Be(100); + results.Messages.Count.Should().Be(1); + + //update an item which did not match the filter and does so after change + items[0].Age = 60; + results.Messages.Count.Should().Be(2); + results.Messages.Last().Refreshes.Should().Be(1); + results.Messages.Last().First().Item.Reason.Should().Be(ListChangeReason.Refresh); + results.Messages.Last().First().Item.Current.Index.Should().Be(0); + + items[60].Age = 160; + results.Messages.Count.Should().Be(3); + results.Messages.Last().Refreshes.Should().Be(1); + results.Messages.Last().First().Item.Reason.Should().Be(ListChangeReason.Refresh); + results.Messages.Last().First().Item.Current.Index.Should().Be(60); + } + } + + [Fact] + public void AutoRefreshSort() + { + var items = Enumerable.Range(1, 100) + .Select(i => new Person("Person" + i, i)) + .OrderByDescending(p=>p.Age) + .ToArray(); + + var comparer = SortExpressionComparer.Ascending(p => p.Age); + + //result should only be true when all items are set to true + using (var list = new SourceList()) + using (var results = list.Connect() + .AutoRefresh(p => p.Age) + .Sort(SortExpressionComparer.Ascending(p=>p.Age)) + .AsAggregator()) + { + + void CheckOrder() + { + var sorted = items.OrderBy(p => p, comparer).ToArray(); + results.Data.Items.ShouldAllBeEquivalentTo(sorted); + } + + list.AddRange(items); + + results.Data.Count.Should().Be(100); + results.Messages.Count.Should().Be(1); + CheckOrder(); + + items[0].Age = 60; + CheckOrder(); + results.Messages.Count.Should().Be(2); + results.Messages.Last().Refreshes.Should().Be(1); + results.Messages.Last().Moves.Should().Be(1); + + items[90].Age = -1; //move to beginning + CheckOrder(); + results.Messages.Count.Should().Be(3); + results.Messages.Last().Refreshes.Should().Be(1); + results.Messages.Last().Moves.Should().Be(1); + + items[50].Age = 49; //same position so no move + CheckOrder(); + results.Messages.Count.Should().Be(4); + results.Messages.Last().Refreshes.Should().Be(1); + results.Messages.Last().Moves.Should().Be(0); + + items[50].Age = 51; //same position so no move + CheckOrder(); + results.Messages.Count.Should().Be(5); + results.Messages.Last().Refreshes.Should().Be(1); + results.Messages.Last().Moves.Should().Be(1); + } + } + + [Fact] + public void AutoRefreshGroup() + { + var items = Enumerable.Range(1, 100) + .Select(i => new Person("Person" + i, i)) + .ToArray(); + + //result should only be true when all items are set to true + using (var list = new SourceList()) + using (var results = list.Connect() + .AutoRefresh(p => p.Age) + .GroupOn(p=>p.Age % 10) + .AsAggregator()) + { + void CheckContent() + { + foreach (var grouping in items.GroupBy(p => p.Age % 10)) + { + var childGroup = results.Data.Items.Single(g => g.GroupKey == grouping.Key); + var expected = grouping.OrderBy(p => p.Name); + var actual = childGroup.List.Items.OrderBy(p => p.Name); + actual.ShouldAllBeEquivalentTo(expected); + } + } + + list.AddRange(items); + results.Data.Count.Should().Be(10); + results.Messages.Count.Should().Be(1); + CheckContent(); + + //move person from group 1 to 2 + items[0].Age = items[0].Age + 1; + CheckContent(); + + //change the value and move to a grouping which does not yet exist + items[1].Age = -1; + results.Data.Count.Should().Be(11); + results.Data.Items.Last().GroupKey.Should().Be(-1); + results.Data.Items.Last().List.Count.Should().Be(1); + results.Data.Items.First().List.Count.Should().Be(9); + CheckContent(); + + //put the value back where it was and check the group was removed + items[1].Age = 1; + results.Data.Count.Should().Be(10); + CheckContent(); + + + var groupOf3 = results.Data.Items.ElementAt(2); + + IChangeSet changes = null; + groupOf3.List.Connect().Subscribe(c => changes = c); + + //refresh an item which makes it belong to the same group - should then propagate a refresh + items[2].Age = 13; + changes.Should().NotBeNull(); + changes.Count.Should().Be(1); + changes.First().Reason.Should().Be(ListChangeReason.Replace); + changes.First().Item.Current.Should().BeSameAs(items[2]); + } + } + + [Fact] + public void AutoRefreshGroupImmutable() + { + var items = Enumerable.Range(1, 100) + .Select(i => new Person("Person" + i, i)) + .ToArray(); + + //result should only be true when all items are set to true + using (var list = new SourceList()) + using (var results = list.Connect() + .AutoRefresh(p => p.Age) + .GroupWithImmutableState(p => p.Age % 10) + .AsAggregator()) + { + void CheckContent() + { + foreach (var grouping in items.GroupBy(p => p.Age % 10)) + { + var childGroup = results.Data.Items.Single(g => g.Key == grouping.Key); + var expected = grouping.OrderBy(p => p.Name); + var actual = childGroup.Items.OrderBy(p => p.Name); + actual.ShouldAllBeEquivalentTo(expected); + } + } + + list.AddRange(items); + results.Data.Count.Should().Be(10); + results.Messages.Count.Should().Be(1); + CheckContent(); + + //move person from group 1 to 2 + items[0].Age = items[0].Age + 1; + CheckContent(); + + //change the value and move to a grouping which does not yet exist + items[1].Age = -1; + results.Data.Count.Should().Be(11); + results.Data.Items.Last().Key.Should().Be(-1); + results.Data.Items.Last().Count.Should().Be(1); + results.Data.Items.First().Count.Should().Be(9); + CheckContent(); + + //put the value back where it was and check the group was removed + items[1].Age = 1; + results.Data.Count.Should().Be(10); + results.Messages.Count.Should().Be(4); + CheckContent(); + + //refresh an item which makes it belong to the same group - should then propagate a refresh + items[2].Age = 13; + CheckContent(); + + results.Messages.Count.Should().Be(5); + } + } + + + [Fact] + public void AutoRefreshDistinct() + { + var items = Enumerable.Range(1, 100) + .Select(i => new Person("Person" + i, i)) + .ToArray(); + + //result should only be true when all items are set to true + using (var list = new SourceList()) + using (var results = list.Connect() + .AutoRefresh(p => p.Age) + .DistinctValues(p => p.Age / 10) + .AsAggregator()) + { + list.AddRange(items); + + results.Data.Count.Should().Be(11); + results.Messages.Count.Should().Be(1); + + //update an item which did not match the filter and does so after change + items[50].Age = 500; + results.Data.Count.Should().Be(12); + + results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Add); + results.Messages.Last().First().Item.Current.Should().Be(50); + } + } + + private class TransformedPerson + { + public Person Person { get; } + public int Index { get; } + public DateTime TimeStamp { get; } = DateTime.Now; + + public TransformedPerson(Person person, int index) + { + Person = person; + Index = index; + } + } + + [Fact] + public void AutoRefreshSelected() + { + //test added as v6 broke unit test in DynamicData.Snippets + var initialItems = Enumerable.Range(1, 10) + .Select(i => new SelectableItem(i)) + .ToArray(); + + //result should only be true when all items are set to true + using (var sourceList = new SourceList()) + using (var sut = sourceList.Connect().AutoRefresh().Filter(si => si.IsSelected).AsObservableList()) + { + sourceList.AddRange(initialItems); + sut.Count.Should().Be(0); + + initialItems[0].IsSelected = true; + sut.Count.Should().Be(1); + + initialItems[1].IsSelected = true; + sut.Count.Should().Be(2); + + //remove the selected items + sourceList.RemoveRange(0, 2); + sut.Count.Should().Be(0); + } + } + + private class SelectableItem : AbstractNotifyPropertyChanged + { + public int Id { get; } + + public SelectableItem(int id) + { + Id = id; + } + + private bool _isSelected; + + public bool IsSelected + { + get => _isSelected; + set => SetAndRaise(ref _isSelected, value); + } + + protected bool Equals(SelectableItem other) + { + return Id == other.Id; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((SelectableItem)obj); + } + + public override int GetHashCode() + { + return Id; + } + } + + [Fact] + public void RefreshTransformAsList() + { + SourceList list = new SourceList(); + var valueList = list.Connect() + .AutoRefresh(e => e.Value) + .Transform(e => e.Value, true) + .AsObservableList(); + + var obj = new Example { Value = 0 }; + list.Add(obj); + obj.Value = 1; + valueList.Items.First().Should().Be(1); + } + + private class Example : AbstractNotifyPropertyChanged + { + private int _value; + + public int Value + { + get => _value; + set => SetAndRaise(ref _value, value); + } + } + + } +} diff --git a/DynamicData.Tests/List/BatchFixture.cs b/src/DynamicData.Tests/List/BatchFixture.cs similarity index 100% rename from DynamicData.Tests/List/BatchFixture.cs rename to src/DynamicData.Tests/List/BatchFixture.cs diff --git a/DynamicData.Tests/List/BatchIfFixture.cs b/src/DynamicData.Tests/List/BatchIfFixture.cs similarity index 100% rename from DynamicData.Tests/List/BatchIfFixture.cs rename to src/DynamicData.Tests/List/BatchIfFixture.cs diff --git a/DynamicData.Tests/List/BatchIfWithTimeOutFixture.cs b/src/DynamicData.Tests/List/BatchIfWithTimeOutFixture.cs similarity index 100% rename from DynamicData.Tests/List/BatchIfWithTimeOutFixture.cs rename to src/DynamicData.Tests/List/BatchIfWithTimeOutFixture.cs diff --git a/DynamicData.Tests/List/BufferFixture.cs b/src/DynamicData.Tests/List/BufferFixture.cs similarity index 100% rename from DynamicData.Tests/List/BufferFixture.cs rename to src/DynamicData.Tests/List/BufferFixture.cs diff --git a/DynamicData.Tests/List/BufferInitialFixture.cs b/src/DynamicData.Tests/List/BufferInitialFixture.cs similarity index 100% rename from DynamicData.Tests/List/BufferInitialFixture.cs rename to src/DynamicData.Tests/List/BufferInitialFixture.cs diff --git a/DynamicData.Tests/List/CastFixture.cs b/src/DynamicData.Tests/List/CastFixture.cs similarity index 100% rename from DynamicData.Tests/List/CastFixture.cs rename to src/DynamicData.Tests/List/CastFixture.cs diff --git a/DynamicData.Tests/List/ChangeAwareListFixture.cs b/src/DynamicData.Tests/List/ChangeAwareListFixture.cs similarity index 100% rename from DynamicData.Tests/List/ChangeAwareListFixture.cs rename to src/DynamicData.Tests/List/ChangeAwareListFixture.cs diff --git a/DynamicData.Tests/List/CloneChangesFixture.cs b/src/DynamicData.Tests/List/CloneChangesFixture.cs similarity index 100% rename from DynamicData.Tests/List/CloneChangesFixture.cs rename to src/DynamicData.Tests/List/CloneChangesFixture.cs diff --git a/DynamicData.Tests/List/CloneFixture.cs b/src/DynamicData.Tests/List/CloneFixture.cs similarity index 100% rename from DynamicData.Tests/List/CloneFixture.cs rename to src/DynamicData.Tests/List/CloneFixture.cs diff --git a/DynamicData.Tests/List/CreationFixtures.cs b/src/DynamicData.Tests/List/CreationFixtures.cs similarity index 96% rename from DynamicData.Tests/List/CreationFixtures.cs rename to src/DynamicData.Tests/List/CreationFixtures.cs index d7a6e1cc5..2793fdd99 100644 --- a/DynamicData.Tests/List/CreationFixtures.cs +++ b/src/DynamicData.Tests/List/CreationFixtures.cs @@ -1,49 +1,49 @@ -using System; -using System.Reactive.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using Xunit; - -namespace DynamicData.Tests.List -{ - - public class ListCreationFixtures - { - - [Fact] - public void Create() - { - Task CreateTask(T value) => Task.FromResult(value); - - SubscribeAndAssert(ObservableChangeSet.Create(async list => - { - var value = await CreateTask(10); - list.Add(value); - return () => { }; - })); - } - - private void SubscribeAndAssert(IObservable> observableChangeset, bool expectsError = false) - { - Exception error = null; - bool complete = false; - IChangeSet changes = null; - - using (var myList = observableChangeset - .Finally(()=> complete = true) - .AsObservableList()) - using (myList.Connect().Subscribe(result => changes = result, ex => error = ex)) - { - if (!expectsError) - { - error.Should().BeNull(); - } - else - { - error.Should().NotBeNull(); - } - } - complete.Should().BeTrue(); - } - } -} +using System; +using System.Reactive.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; + +namespace DynamicData.Tests.List +{ + + public class ListCreationFixtures + { + + [Fact] + public void Create() + { + Task CreateTask(T value) => Task.FromResult(value); + + SubscribeAndAssert(ObservableChangeSet.Create(async list => + { + var value = await CreateTask(10); + list.Add(value); + return () => { }; + })); + } + + private void SubscribeAndAssert(IObservable> observableChangeset, bool expectsError = false) + { + Exception error = null; + bool complete = false; + IChangeSet changes = null; + + using (var myList = observableChangeset + .Finally(()=> complete = true) + .AsObservableList()) + using (myList.Connect().Subscribe(result => changes = result, ex => error = ex)) + { + if (!expectsError) + { + error.Should().BeNull(); + } + else + { + error.Should().NotBeNull(); + } + } + complete.Should().BeTrue(); + } + } +} diff --git a/DynamicData.Tests/List/DeferUntilLoadedFixture.cs b/src/DynamicData.Tests/List/DeferUntilLoadedFixture.cs similarity index 100% rename from DynamicData.Tests/List/DeferUntilLoadedFixture.cs rename to src/DynamicData.Tests/List/DeferUntilLoadedFixture.cs diff --git a/DynamicData.Tests/List/DisposeManyFixture.cs b/src/DynamicData.Tests/List/DisposeManyFixture.cs similarity index 100% rename from DynamicData.Tests/List/DisposeManyFixture.cs rename to src/DynamicData.Tests/List/DisposeManyFixture.cs diff --git a/DynamicData.Tests/List/DistinctValuesFixture.cs b/src/DynamicData.Tests/List/DistinctValuesFixture.cs similarity index 100% rename from DynamicData.Tests/List/DistinctValuesFixture.cs rename to src/DynamicData.Tests/List/DistinctValuesFixture.cs diff --git a/DynamicData.Tests/List/DynamicAndFixture.cs b/src/DynamicData.Tests/List/DynamicAndFixture.cs similarity index 100% rename from DynamicData.Tests/List/DynamicAndFixture.cs rename to src/DynamicData.Tests/List/DynamicAndFixture.cs diff --git a/DynamicData.Tests/List/DynamicExceptFixture.cs b/src/DynamicData.Tests/List/DynamicExceptFixture.cs similarity index 100% rename from DynamicData.Tests/List/DynamicExceptFixture.cs rename to src/DynamicData.Tests/List/DynamicExceptFixture.cs diff --git a/DynamicData.Tests/List/DynamicOrFixture.cs b/src/DynamicData.Tests/List/DynamicOrFixture.cs similarity index 100% rename from DynamicData.Tests/List/DynamicOrFixture.cs rename to src/DynamicData.Tests/List/DynamicOrFixture.cs diff --git a/DynamicData.Tests/List/DynamicXOrFixture.cs b/src/DynamicData.Tests/List/DynamicXOrFixture.cs similarity index 100% rename from DynamicData.Tests/List/DynamicXOrFixture.cs rename to src/DynamicData.Tests/List/DynamicXOrFixture.cs diff --git a/DynamicData.Tests/List/EditDiffFixture.cs b/src/DynamicData.Tests/List/EditDiffFixture.cs similarity index 100% rename from DynamicData.Tests/List/EditDiffFixture.cs rename to src/DynamicData.Tests/List/EditDiffFixture.cs diff --git a/DynamicData.Tests/List/ExceptFixture.cs b/src/DynamicData.Tests/List/ExceptFixture.cs similarity index 100% rename from DynamicData.Tests/List/ExceptFixture.cs rename to src/DynamicData.Tests/List/ExceptFixture.cs diff --git a/DynamicData.Tests/List/ExpireAfterFixture.cs b/src/DynamicData.Tests/List/ExpireAfterFixture.cs similarity index 100% rename from DynamicData.Tests/List/ExpireAfterFixture.cs rename to src/DynamicData.Tests/List/ExpireAfterFixture.cs diff --git a/DynamicData.Tests/List/FilterControllerFixtureWithClearAndReplace.cs b/src/DynamicData.Tests/List/FilterControllerFixtureWithClearAndReplace.cs similarity index 100% rename from DynamicData.Tests/List/FilterControllerFixtureWithClearAndReplace.cs rename to src/DynamicData.Tests/List/FilterControllerFixtureWithClearAndReplace.cs diff --git a/DynamicData.Tests/List/FilterControllerFixtureWithDiffSet.cs b/src/DynamicData.Tests/List/FilterControllerFixtureWithDiffSet.cs similarity index 100% rename from DynamicData.Tests/List/FilterControllerFixtureWithDiffSet.cs rename to src/DynamicData.Tests/List/FilterControllerFixtureWithDiffSet.cs diff --git a/DynamicData.Tests/List/FilterFixture.cs b/src/DynamicData.Tests/List/FilterFixture.cs similarity index 100% rename from DynamicData.Tests/List/FilterFixture.cs rename to src/DynamicData.Tests/List/FilterFixture.cs diff --git a/DynamicData.Tests/List/FilterOnObservableFixture.cs b/src/DynamicData.Tests/List/FilterOnObservableFixture.cs similarity index 100% rename from DynamicData.Tests/List/FilterOnObservableFixture.cs rename to src/DynamicData.Tests/List/FilterOnObservableFixture.cs diff --git a/DynamicData.Tests/List/FilterOnPropertyFixture.cs b/src/DynamicData.Tests/List/FilterOnPropertyFixture.cs similarity index 100% rename from DynamicData.Tests/List/FilterOnPropertyFixture.cs rename to src/DynamicData.Tests/List/FilterOnPropertyFixture.cs diff --git a/DynamicData.Tests/List/FilterWithObservable.cs b/src/DynamicData.Tests/List/FilterWithObservable.cs similarity index 100% rename from DynamicData.Tests/List/FilterWithObservable.cs rename to src/DynamicData.Tests/List/FilterWithObservable.cs diff --git a/DynamicData.Tests/List/ForEachChangeFixture.cs b/src/DynamicData.Tests/List/ForEachChangeFixture.cs similarity index 100% rename from DynamicData.Tests/List/ForEachChangeFixture.cs rename to src/DynamicData.Tests/List/ForEachChangeFixture.cs diff --git a/DynamicData.Tests/List/FromAsyncFixture.cs b/src/DynamicData.Tests/List/FromAsyncFixture.cs similarity index 100% rename from DynamicData.Tests/List/FromAsyncFixture.cs rename to src/DynamicData.Tests/List/FromAsyncFixture.cs diff --git a/DynamicData.Tests/List/GroupImmutableFixture.cs b/src/DynamicData.Tests/List/GroupImmutableFixture.cs similarity index 100% rename from DynamicData.Tests/List/GroupImmutableFixture.cs rename to src/DynamicData.Tests/List/GroupImmutableFixture.cs diff --git a/DynamicData.Tests/List/GroupOnFixture.cs b/src/DynamicData.Tests/List/GroupOnFixture.cs similarity index 100% rename from DynamicData.Tests/List/GroupOnFixture.cs rename to src/DynamicData.Tests/List/GroupOnFixture.cs diff --git a/DynamicData.Tests/List/GroupOnPropertyFixture.cs b/src/DynamicData.Tests/List/GroupOnPropertyFixture.cs similarity index 100% rename from DynamicData.Tests/List/GroupOnPropertyFixture.cs rename to src/DynamicData.Tests/List/GroupOnPropertyFixture.cs diff --git a/DynamicData.Tests/List/GroupOnPropertyWithImmutableStateFixture.cs b/src/DynamicData.Tests/List/GroupOnPropertyWithImmutableStateFixture.cs similarity index 100% rename from DynamicData.Tests/List/GroupOnPropertyWithImmutableStateFixture.cs rename to src/DynamicData.Tests/List/GroupOnPropertyWithImmutableStateFixture.cs diff --git a/DynamicData.Tests/List/MergeManyChangeSetsFixture.cs b/src/DynamicData.Tests/List/MergeManyChangeSetsFixture.cs similarity index 100% rename from DynamicData.Tests/List/MergeManyChangeSetsFixture.cs rename to src/DynamicData.Tests/List/MergeManyChangeSetsFixture.cs diff --git a/DynamicData.Tests/List/MergeManyFixture.cs b/src/DynamicData.Tests/List/MergeManyFixture.cs similarity index 100% rename from DynamicData.Tests/List/MergeManyFixture.cs rename to src/DynamicData.Tests/List/MergeManyFixture.cs diff --git a/DynamicData.Tests/List/OrFixture.cs b/src/DynamicData.Tests/List/OrFixture.cs similarity index 100% rename from DynamicData.Tests/List/OrFixture.cs rename to src/DynamicData.Tests/List/OrFixture.cs diff --git a/DynamicData.Tests/List/PageFixture.cs b/src/DynamicData.Tests/List/PageFixture.cs similarity index 100% rename from DynamicData.Tests/List/PageFixture.cs rename to src/DynamicData.Tests/List/PageFixture.cs diff --git a/DynamicData.Tests/List/QueryWhenChangedFixture.cs b/src/DynamicData.Tests/List/QueryWhenChangedFixture.cs similarity index 100% rename from DynamicData.Tests/List/QueryWhenChangedFixture.cs rename to src/DynamicData.Tests/List/QueryWhenChangedFixture.cs diff --git a/DynamicData.Tests/List/RecursiveTransformManyFixture.cs b/src/DynamicData.Tests/List/RecursiveTransformManyFixture.cs similarity index 100% rename from DynamicData.Tests/List/RecursiveTransformManyFixture.cs rename to src/DynamicData.Tests/List/RecursiveTransformManyFixture.cs diff --git a/DynamicData.Tests/List/RefCountFixture.cs b/src/DynamicData.Tests/List/RefCountFixture.cs similarity index 100% rename from DynamicData.Tests/List/RefCountFixture.cs rename to src/DynamicData.Tests/List/RefCountFixture.cs diff --git a/DynamicData.Tests/List/RemoveManyFixture.cs b/src/DynamicData.Tests/List/RemoveManyFixture.cs similarity index 100% rename from DynamicData.Tests/List/RemoveManyFixture.cs rename to src/DynamicData.Tests/List/RemoveManyFixture.cs diff --git a/DynamicData.Tests/List/ReverseFixture.cs b/src/DynamicData.Tests/List/ReverseFixture.cs similarity index 100% rename from DynamicData.Tests/List/ReverseFixture.cs rename to src/DynamicData.Tests/List/ReverseFixture.cs diff --git a/DynamicData.Tests/List/SelectFixture.cs b/src/DynamicData.Tests/List/SelectFixture.cs similarity index 100% rename from DynamicData.Tests/List/SelectFixture.cs rename to src/DynamicData.Tests/List/SelectFixture.cs diff --git a/DynamicData.Tests/List/SizeLimitFixture.cs b/src/DynamicData.Tests/List/SizeLimitFixture.cs similarity index 100% rename from DynamicData.Tests/List/SizeLimitFixture.cs rename to src/DynamicData.Tests/List/SizeLimitFixture.cs diff --git a/DynamicData.Tests/List/SortFixture.cs b/src/DynamicData.Tests/List/SortFixture.cs similarity index 100% rename from DynamicData.Tests/List/SortFixture.cs rename to src/DynamicData.Tests/List/SortFixture.cs diff --git a/DynamicData.Tests/List/SortMutableFixture.cs b/src/DynamicData.Tests/List/SortMutableFixture.cs similarity index 100% rename from DynamicData.Tests/List/SortMutableFixture.cs rename to src/DynamicData.Tests/List/SortMutableFixture.cs diff --git a/DynamicData.Tests/List/SortPrimitiveFixture.cs b/src/DynamicData.Tests/List/SortPrimitiveFixture.cs similarity index 100% rename from DynamicData.Tests/List/SortPrimitiveFixture.cs rename to src/DynamicData.Tests/List/SortPrimitiveFixture.cs diff --git a/DynamicData.Tests/List/SourceListPreviewFixture.cs b/src/DynamicData.Tests/List/SourceListPreviewFixture.cs similarity index 100% rename from DynamicData.Tests/List/SourceListPreviewFixture.cs rename to src/DynamicData.Tests/List/SourceListPreviewFixture.cs diff --git a/DynamicData.Tests/List/SubscribeManyFixture.cs b/src/DynamicData.Tests/List/SubscribeManyFixture.cs similarity index 100% rename from DynamicData.Tests/List/SubscribeManyFixture.cs rename to src/DynamicData.Tests/List/SubscribeManyFixture.cs diff --git a/DynamicData.Tests/List/SwitchFixture.cs b/src/DynamicData.Tests/List/SwitchFixture.cs similarity index 100% rename from DynamicData.Tests/List/SwitchFixture.cs rename to src/DynamicData.Tests/List/SwitchFixture.cs diff --git a/DynamicData.Tests/List/ToObservableChangeSetFixture.cs b/src/DynamicData.Tests/List/ToObservableChangeSetFixture.cs similarity index 100% rename from DynamicData.Tests/List/ToObservableChangeSetFixture.cs rename to src/DynamicData.Tests/List/ToObservableChangeSetFixture.cs diff --git a/DynamicData.Tests/List/TransformAsyncFixture.cs b/src/DynamicData.Tests/List/TransformAsyncFixture.cs similarity index 97% rename from DynamicData.Tests/List/TransformAsyncFixture.cs rename to src/DynamicData.Tests/List/TransformAsyncFixture.cs index ba0af4c55..310484cf2 100644 --- a/DynamicData.Tests/List/TransformAsyncFixture.cs +++ b/src/DynamicData.Tests/List/TransformAsyncFixture.cs @@ -1,125 +1,125 @@ -using System; -using System.Threading.Tasks; -using DynamicData.Tests.Domain; - - -namespace DynamicData.Tests.List -{ - - [Obsolete("Not obsolete - test commented out due to test run freezing on Appveyor")] - public class TransformAsyncFixture: IDisposable - { - private ISourceList _source; - private ChangeSetAggregator _results; - - private readonly Func> _transformFactory = p => - { - var gender = p.Age % 2 == 0 ? "M" : "F"; - var transformed = new PersonWithGender(p, gender); - return Task.FromResult(transformed); - }; - - public TransformAsyncFixture() - { - _source = new SourceList(); - _results = new ChangeSetAggregator(_source.Connect().TransformAsync(_transformFactory)); - } - - public void Dispose() - { - _source.Dispose(); - _results.Dispose(); - } - - /* - - */ - - //[Fact] - //public async Task Add() - //{ - // var person = new Person("Adult1", 50); - // _source.Add(person); - - // _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // _results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); - - // var transformed = await _transformFactory(person); - // _results.Data.Items.First().Should().Be(transformed, "Should be same person"); - //} - - //[Fact] - //public void Remove() - //{ - // const string key = "Adult1"; - // var person = new Person(key, 50); - - // _source.Add(person); - // _source.Remove(person); - - // _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // _results.Messages[0].Adds.Should().Be(1, "Should be 80 addes"); - // _results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); - // _results.Data.Count.Should().Be(0, "Should be nothing cached"); - //} - - //[Fact] - //public void Update() - //{ - // const string key = "Adult1"; - // var newperson = new Person(key, 50); - // var updated = new Person(key, 51); - - // _source.Add(newperson); - // _source.Add(updated); - - // _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); - // _results.Messages[0].Replaced.Should().Be(0, "Should be 1 update"); - //} - - //[Fact] - //public async Task BatchOfUniqueUpdates() - //{ - // var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - // _source.AddRange(people); - - // _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // _results.Messages[0].Adds.Should().Be(100, "Should return 100 adds"); - - // var tasks = people.Select(_transformFactory); - // var result = await Task.WhenAll(tasks); - - // var transformed = result.OrderBy(p => p.Age).ToArray(); - // _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect transform result"); - //} - - //[Fact] - //public void SameKeyChanges() - //{ - // var people = Enumerable.Range(1, 10).Select(i => new Person("Name", i)).ToArray(); - - // _source.AddRange(people); - - // _results.Messages.Count.Should().Be(1, "Should be 1 updates"); - // _results.Messages[0].Adds.Should().Be(10, "Should return 10 adds"); - // _results.Data.Count.Should().Be(10, "Should result in 10 records"); - //} - - //[Fact] - //public void Clear() - //{ - // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); - - // _source.AddRange(people); - // _source.Clear(); - - // _results.Messages.Count.Should().Be(2, "Should be 2 updates"); - // _results.Messages[0].Adds.Should().Be(100, "Should be 80 addes"); - // _results.Messages[1].Removes.Should().Be(100, "Should be 80 removes"); - // _results.Data.Count.Should().Be(0, "Should be nothing cached"); - //} - } +using System; +using System.Threading.Tasks; +using DynamicData.Tests.Domain; + + +namespace DynamicData.Tests.List +{ + + [Obsolete("Not obsolete - test commented out due to test run freezing on Appveyor")] + public class TransformAsyncFixture: IDisposable + { + private ISourceList _source; + private ChangeSetAggregator _results; + + private readonly Func> _transformFactory = p => + { + var gender = p.Age % 2 == 0 ? "M" : "F"; + var transformed = new PersonWithGender(p, gender); + return Task.FromResult(transformed); + }; + + public TransformAsyncFixture() + { + _source = new SourceList(); + _results = new ChangeSetAggregator(_source.Connect().TransformAsync(_transformFactory)); + } + + public void Dispose() + { + _source.Dispose(); + _results.Dispose(); + } + + /* + + */ + + //[Fact] + //public async Task Add() + //{ + // var person = new Person("Adult1", 50); + // _source.Add(person); + + // _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // _results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); + + // var transformed = await _transformFactory(person); + // _results.Data.Items.First().Should().Be(transformed, "Should be same person"); + //} + + //[Fact] + //public void Remove() + //{ + // const string key = "Adult1"; + // var person = new Person(key, 50); + + // _source.Add(person); + // _source.Remove(person); + + // _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // _results.Messages[0].Adds.Should().Be(1, "Should be 80 addes"); + // _results.Messages[1].Removes.Should().Be(1, "Should be 80 removes"); + // _results.Data.Count.Should().Be(0, "Should be nothing cached"); + //} + + //[Fact] + //public void Update() + //{ + // const string key = "Adult1"; + // var newperson = new Person(key, 50); + // var updated = new Person(key, 51); + + // _source.Add(newperson); + // _source.Add(updated); + + // _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // _results.Messages[0].Adds.Should().Be(1, "Should be 1 adds"); + // _results.Messages[0].Replaced.Should().Be(0, "Should be 1 update"); + //} + + //[Fact] + //public async Task BatchOfUniqueUpdates() + //{ + // var people = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); + + // _source.AddRange(people); + + // _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // _results.Messages[0].Adds.Should().Be(100, "Should return 100 adds"); + + // var tasks = people.Select(_transformFactory); + // var result = await Task.WhenAll(tasks); + + // var transformed = result.OrderBy(p => p.Age).ToArray(); + // _results.Data.Items.OrderBy(p => p.Age).ShouldAllBeEquivalentTo(_results.Data.Items.OrderBy(p => p.Age), "Incorrect transform result"); + //} + + //[Fact] + //public void SameKeyChanges() + //{ + // var people = Enumerable.Range(1, 10).Select(i => new Person("Name", i)).ToArray(); + + // _source.AddRange(people); + + // _results.Messages.Count.Should().Be(1, "Should be 1 updates"); + // _results.Messages[0].Adds.Should().Be(10, "Should return 10 adds"); + // _results.Data.Count.Should().Be(10, "Should result in 10 records"); + //} + + //[Fact] + //public void Clear() + //{ + // var people = Enumerable.Range(1, 100).Select(l => new Person("Name" + l, l)).ToArray(); + + // _source.AddRange(people); + // _source.Clear(); + + // _results.Messages.Count.Should().Be(2, "Should be 2 updates"); + // _results.Messages[0].Adds.Should().Be(100, "Should be 80 addes"); + // _results.Messages[1].Removes.Should().Be(100, "Should be 80 removes"); + // _results.Data.Count.Should().Be(0, "Should be nothing cached"); + //} + } } \ No newline at end of file diff --git a/DynamicData.Tests/List/TransformFixture.cs b/src/DynamicData.Tests/List/TransformFixture.cs similarity index 100% rename from DynamicData.Tests/List/TransformFixture.cs rename to src/DynamicData.Tests/List/TransformFixture.cs diff --git a/DynamicData.Tests/List/TransformManyFixture.cs b/src/DynamicData.Tests/List/TransformManyFixture.cs similarity index 100% rename from DynamicData.Tests/List/TransformManyFixture.cs rename to src/DynamicData.Tests/List/TransformManyFixture.cs diff --git a/DynamicData.Tests/List/TransformManyObservableCollectionFixture.cs b/src/DynamicData.Tests/List/TransformManyObservableCollectionFixture.cs similarity index 100% rename from DynamicData.Tests/List/TransformManyObservableCollectionFixture.cs rename to src/DynamicData.Tests/List/TransformManyObservableCollectionFixture.cs diff --git a/DynamicData.Tests/List/TransformManyRefreshFixture.cs b/src/DynamicData.Tests/List/TransformManyRefreshFixture.cs similarity index 100% rename from DynamicData.Tests/List/TransformManyRefreshFixture.cs rename to src/DynamicData.Tests/List/TransformManyRefreshFixture.cs diff --git a/DynamicData.Tests/List/VirtualisationFixture.cs b/src/DynamicData.Tests/List/VirtualisationFixture.cs similarity index 100% rename from DynamicData.Tests/List/VirtualisationFixture.cs rename to src/DynamicData.Tests/List/VirtualisationFixture.cs diff --git a/DynamicData.Tests/List/XOrFixture.cs b/src/DynamicData.Tests/List/XOrFixture.cs similarity index 100% rename from DynamicData.Tests/List/XOrFixture.cs rename to src/DynamicData.Tests/List/XOrFixture.cs diff --git a/DynamicData.Tests/Utilities/SelectManyExtensions.cs b/src/DynamicData.Tests/Utilities/SelectManyExtensions.cs similarity index 100% rename from DynamicData.Tests/Utilities/SelectManyExtensions.cs rename to src/DynamicData.Tests/Utilities/SelectManyExtensions.cs diff --git a/DynamicData.sln b/src/DynamicData.sln similarity index 98% rename from DynamicData.sln rename to src/DynamicData.sln index ec27a0088..08732c71e 100644 --- a/DynamicData.sln +++ b/src/DynamicData.sln @@ -1,140 +1,140 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.3 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData", "DynamicData\DynamicData.csproj", "{FE903921-6C55-40E3-9A16-4127ECACC12C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".other", ".other", "{DFCC45F8-251C-4D78-AF9B-15C1EDF0E603}" - ProjectSection(SolutionItems) = preProject - appveyor.yml = appveyor.yml - Directory.Build.props = Directory.Build.props - Directory.build.targets = Directory.build.targets - global.json = global.json - NuGet.Config = NuGet.Config - README.md = README.md - ReleaseNotes.md = ReleaseNotes.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Tests", "DynamicData.Tests\DynamicData.Tests.csproj", "{1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.ReactiveUI", "DynamicData.ReactiveUI\DynamicData.ReactiveUI.csproj", "{EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.ReactiveUI.Tests", "DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj", "{9F0897D7-B92A-4162-AEA5-C143614C5904}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Profile", "DynamicData.Profile\DynamicData.Profile.csproj", "{7A1B3B5B-50A0-491F-BC2F-513299DEE82D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Benchmarks", "DynamicData.Benchmarks\DynamicData.Benchmarks.csproj", "{42566F48-05FC-483E-8B2F-D0EA4F28E870}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|x64.ActiveCfg = Debug|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|x86.ActiveCfg = Debug|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|Any CPU.Build.0 = Release|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|x64.ActiveCfg = Release|Any CPU - {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|x86.ActiveCfg = Release|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|x64.ActiveCfg = Debug|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|x64.Build.0 = Debug|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|x86.ActiveCfg = Debug|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|x86.Build.0 = Debug|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|Any CPU.Build.0 = Release|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x64.ActiveCfg = Release|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x64.Build.0 = Release|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x86.ActiveCfg = Release|Any CPU - {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x86.Build.0 = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x64.ActiveCfg = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x64.Build.0 = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x86.ActiveCfg = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x86.Build.0 = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Any CPU.Build.0 = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x64.ActiveCfg = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x64.Build.0 = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x86.ActiveCfg = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x86.Build.0 = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x64.ActiveCfg = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x64.Build.0 = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x86.ActiveCfg = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x86.Build.0 = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Any CPU.Build.0 = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x64.ActiveCfg = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x64.Build.0 = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x86.ActiveCfg = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x86.Build.0 = Release|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|x64.ActiveCfg = Debug|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|x64.Build.0 = Debug|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|x86.ActiveCfg = Debug|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|x86.Build.0 = Debug|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|Any CPU.Build.0 = Release|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|x64.ActiveCfg = Release|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|x64.Build.0 = Release|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|x86.ActiveCfg = Release|Any CPU - {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|x86.Build.0 = Release|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|x64.ActiveCfg = Debug|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|x64.Build.0 = Debug|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|x86.ActiveCfg = Debug|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|x86.Build.0 = Debug|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|Any CPU.Build.0 = Release|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|x64.ActiveCfg = Release|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|x64.Build.0 = Release|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|x86.ActiveCfg = Release|Any CPU - {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {2495D109-BD85-4568-9B27-06D36E6EF562} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData", "DynamicData\DynamicData.csproj", "{FE903921-6C55-40E3-9A16-4127ECACC12C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".other", ".other", "{DFCC45F8-251C-4D78-AF9B-15C1EDF0E603}" + ProjectSection(SolutionItems) = preProject + appveyor.yml = appveyor.yml + Directory.Build.props = Directory.Build.props + Directory.build.targets = Directory.build.targets + global.json = global.json + NuGet.Config = NuGet.Config + README.md = README.md + ReleaseNotes.md = ReleaseNotes.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Tests", "DynamicData.Tests\DynamicData.Tests.csproj", "{1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.ReactiveUI", "DynamicData.ReactiveUI\DynamicData.ReactiveUI.csproj", "{EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.ReactiveUI.Tests", "DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj", "{9F0897D7-B92A-4162-AEA5-C143614C5904}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Profile", "DynamicData.Profile\DynamicData.Profile.csproj", "{7A1B3B5B-50A0-491F-BC2F-513299DEE82D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Benchmarks", "DynamicData.Benchmarks\DynamicData.Benchmarks.csproj", "{42566F48-05FC-483E-8B2F-D0EA4F28E870}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|x64.ActiveCfg = Debug|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Debug|x86.ActiveCfg = Debug|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|Any CPU.Build.0 = Release|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|x64.ActiveCfg = Release|Any CPU + {FE903921-6C55-40E3-9A16-4127ECACC12C}.Release|x86.ActiveCfg = Release|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|x64.ActiveCfg = Debug|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|x64.Build.0 = Debug|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|x86.ActiveCfg = Debug|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Debug|x86.Build.0 = Debug|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|Any CPU.Build.0 = Release|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x64.ActiveCfg = Release|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x64.Build.0 = Release|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x86.ActiveCfg = Release|Any CPU + {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x86.Build.0 = Release|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x64.ActiveCfg = Debug|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x64.Build.0 = Debug|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x86.ActiveCfg = Debug|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x86.Build.0 = Debug|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Any CPU.Build.0 = Release|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x64.ActiveCfg = Release|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x64.Build.0 = Release|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x86.ActiveCfg = Release|Any CPU + {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x86.Build.0 = Release|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x64.ActiveCfg = Debug|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x64.Build.0 = Debug|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x86.ActiveCfg = Debug|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x86.Build.0 = Debug|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Any CPU.Build.0 = Release|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x64.ActiveCfg = Release|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x64.Build.0 = Release|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x86.ActiveCfg = Release|Any CPU + {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x86.Build.0 = Release|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|x64.ActiveCfg = Debug|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|x64.Build.0 = Debug|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|x86.ActiveCfg = Debug|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|x86.Build.0 = Debug|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|Any CPU.Build.0 = Release|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|x64.ActiveCfg = Release|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|x64.Build.0 = Release|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|x86.ActiveCfg = Release|Any CPU + {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Release|x86.Build.0 = Release|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|x64.ActiveCfg = Debug|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|x64.Build.0 = Debug|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|x86.ActiveCfg = Debug|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Debug|x86.Build.0 = Debug|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|Any CPU.Build.0 = Release|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|x64.ActiveCfg = Release|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|x64.Build.0 = Release|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|x86.ActiveCfg = Release|Any CPU + {42566F48-05FC-483E-8B2F-D0EA4F28E870}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2495D109-BD85-4568-9B27-06D36E6EF562} + EndGlobalSection +EndGlobal diff --git a/DynamicData/Aggregation/AggregateEnumerator.cs b/src/DynamicData/Aggregation/AggregateEnumerator.cs similarity index 100% rename from DynamicData/Aggregation/AggregateEnumerator.cs rename to src/DynamicData/Aggregation/AggregateEnumerator.cs diff --git a/DynamicData/Aggregation/AggregateItem.cs b/src/DynamicData/Aggregation/AggregateItem.cs similarity index 100% rename from DynamicData/Aggregation/AggregateItem.cs rename to src/DynamicData/Aggregation/AggregateItem.cs diff --git a/DynamicData/Aggregation/AggregateType.cs b/src/DynamicData/Aggregation/AggregateType.cs similarity index 100% rename from DynamicData/Aggregation/AggregateType.cs rename to src/DynamicData/Aggregation/AggregateType.cs diff --git a/DynamicData/Aggregation/AggregationEx.cs b/src/DynamicData/Aggregation/AggregationEx.cs similarity index 100% rename from DynamicData/Aggregation/AggregationEx.cs rename to src/DynamicData/Aggregation/AggregationEx.cs diff --git a/DynamicData/Aggregation/Avg.cs b/src/DynamicData/Aggregation/Avg.cs similarity index 100% rename from DynamicData/Aggregation/Avg.cs rename to src/DynamicData/Aggregation/Avg.cs diff --git a/DynamicData/Aggregation/AvgEx.cs b/src/DynamicData/Aggregation/AvgEx.cs similarity index 100% rename from DynamicData/Aggregation/AvgEx.cs rename to src/DynamicData/Aggregation/AvgEx.cs diff --git a/DynamicData/Aggregation/CountEx.cs b/src/DynamicData/Aggregation/CountEx.cs similarity index 100% rename from DynamicData/Aggregation/CountEx.cs rename to src/DynamicData/Aggregation/CountEx.cs diff --git a/DynamicData/Aggregation/IAggregateChangeSet.cs b/src/DynamicData/Aggregation/IAggregateChangeSet.cs similarity index 100% rename from DynamicData/Aggregation/IAggregateChangeSet.cs rename to src/DynamicData/Aggregation/IAggregateChangeSet.cs diff --git a/DynamicData/Aggregation/MaxEx.cs b/src/DynamicData/Aggregation/MaxEx.cs similarity index 100% rename from DynamicData/Aggregation/MaxEx.cs rename to src/DynamicData/Aggregation/MaxEx.cs diff --git a/DynamicData/Aggregation/StdDev.cs b/src/DynamicData/Aggregation/StdDev.cs similarity index 100% rename from DynamicData/Aggregation/StdDev.cs rename to src/DynamicData/Aggregation/StdDev.cs diff --git a/DynamicData/Aggregation/StdDevEx.cs b/src/DynamicData/Aggregation/StdDevEx.cs similarity index 100% rename from DynamicData/Aggregation/StdDevEx.cs rename to src/DynamicData/Aggregation/StdDevEx.cs diff --git a/DynamicData/Aggregation/SumEx.cs b/src/DynamicData/Aggregation/SumEx.cs similarity index 100% rename from DynamicData/Aggregation/SumEx.cs rename to src/DynamicData/Aggregation/SumEx.cs diff --git a/DynamicData/Alias/ObservableCacheAlias.cs b/src/DynamicData/Alias/ObservableCacheAlias.cs similarity index 100% rename from DynamicData/Alias/ObservableCacheAlias.cs rename to src/DynamicData/Alias/ObservableCacheAlias.cs diff --git a/DynamicData/Alias/ObservableListAlias.cs b/src/DynamicData/Alias/ObservableListAlias.cs similarity index 100% rename from DynamicData/Alias/ObservableListAlias.cs rename to src/DynamicData/Alias/ObservableListAlias.cs diff --git a/DynamicData/Attributes.cs b/src/DynamicData/Attributes.cs similarity index 100% rename from DynamicData/Attributes.cs rename to src/DynamicData/Attributes.cs diff --git a/DynamicData/Binding/AbstractNotifyPropertyChanged.cs b/src/DynamicData/Binding/AbstractNotifyPropertyChanged.cs similarity index 100% rename from DynamicData/Binding/AbstractNotifyPropertyChanged.cs rename to src/DynamicData/Binding/AbstractNotifyPropertyChanged.cs diff --git a/DynamicData/Binding/BindingListAdaptor.cs b/src/DynamicData/Binding/BindingListAdaptor.cs similarity index 100% rename from DynamicData/Binding/BindingListAdaptor.cs rename to src/DynamicData/Binding/BindingListAdaptor.cs diff --git a/DynamicData/Binding/BindingListEventsSuspender.cs b/src/DynamicData/Binding/BindingListEventsSuspender.cs similarity index 100% rename from DynamicData/Binding/BindingListEventsSuspender.cs rename to src/DynamicData/Binding/BindingListEventsSuspender.cs diff --git a/DynamicData/Binding/ExpressionBuilder.cs b/src/DynamicData/Binding/ExpressionBuilder.cs similarity index 100% rename from DynamicData/Binding/ExpressionBuilder.cs rename to src/DynamicData/Binding/ExpressionBuilder.cs diff --git a/DynamicData/Binding/IEvaluateAware.cs b/src/DynamicData/Binding/IEvaluateAware.cs similarity index 100% rename from DynamicData/Binding/IEvaluateAware.cs rename to src/DynamicData/Binding/IEvaluateAware.cs diff --git a/DynamicData/Binding/IIndexAware.cs b/src/DynamicData/Binding/IIndexAware.cs similarity index 100% rename from DynamicData/Binding/IIndexAware.cs rename to src/DynamicData/Binding/IIndexAware.cs diff --git a/DynamicData/Binding/INotifyCollectionChangedSuspender.cs b/src/DynamicData/Binding/INotifyCollectionChangedSuspender.cs similarity index 100% rename from DynamicData/Binding/INotifyCollectionChangedSuspender.cs rename to src/DynamicData/Binding/INotifyCollectionChangedSuspender.cs diff --git a/DynamicData/Binding/IObservableCollection.cs b/src/DynamicData/Binding/IObservableCollection.cs similarity index 100% rename from DynamicData/Binding/IObservableCollection.cs rename to src/DynamicData/Binding/IObservableCollection.cs diff --git a/DynamicData/Binding/IObservableCollectionAdaptor.cs b/src/DynamicData/Binding/IObservableCollectionAdaptor.cs similarity index 100% rename from DynamicData/Binding/IObservableCollectionAdaptor.cs rename to src/DynamicData/Binding/IObservableCollectionAdaptor.cs diff --git a/DynamicData/Binding/ISortedObservableCollectionAdaptor.cs b/src/DynamicData/Binding/ISortedObservableCollectionAdaptor.cs similarity index 100% rename from DynamicData/Binding/ISortedObservableCollectionAdaptor.cs rename to src/DynamicData/Binding/ISortedObservableCollectionAdaptor.cs diff --git a/DynamicData/Binding/NotifyPropertyChangedEx.cs b/src/DynamicData/Binding/NotifyPropertyChangedEx.cs similarity index 100% rename from DynamicData/Binding/NotifyPropertyChangedEx.cs rename to src/DynamicData/Binding/NotifyPropertyChangedEx.cs diff --git a/DynamicData/Binding/ObservableCollectionAdaptor.cs b/src/DynamicData/Binding/ObservableCollectionAdaptor.cs similarity index 100% rename from DynamicData/Binding/ObservableCollectionAdaptor.cs rename to src/DynamicData/Binding/ObservableCollectionAdaptor.cs diff --git a/DynamicData/Binding/ObservableCollectionEx.cs b/src/DynamicData/Binding/ObservableCollectionEx.cs similarity index 100% rename from DynamicData/Binding/ObservableCollectionEx.cs rename to src/DynamicData/Binding/ObservableCollectionEx.cs diff --git a/DynamicData/Binding/ObservableCollectionExtended.cs b/src/DynamicData/Binding/ObservableCollectionExtended.cs similarity index 100% rename from DynamicData/Binding/ObservableCollectionExtended.cs rename to src/DynamicData/Binding/ObservableCollectionExtended.cs diff --git a/DynamicData/Binding/ObservablePropertyFactory.cs b/src/DynamicData/Binding/ObservablePropertyFactory.cs similarity index 100% rename from DynamicData/Binding/ObservablePropertyFactory.cs rename to src/DynamicData/Binding/ObservablePropertyFactory.cs diff --git a/DynamicData/Binding/ObservablePropertyFactoryCache.cs b/src/DynamicData/Binding/ObservablePropertyFactoryCache.cs similarity index 100% rename from DynamicData/Binding/ObservablePropertyFactoryCache.cs rename to src/DynamicData/Binding/ObservablePropertyFactoryCache.cs diff --git a/DynamicData/Binding/ObservablePropertyPart.cs b/src/DynamicData/Binding/ObservablePropertyPart.cs similarity index 100% rename from DynamicData/Binding/ObservablePropertyPart.cs rename to src/DynamicData/Binding/ObservablePropertyPart.cs diff --git a/DynamicData/Binding/PropertyValue.cs b/src/DynamicData/Binding/PropertyValue.cs similarity index 100% rename from DynamicData/Binding/PropertyValue.cs rename to src/DynamicData/Binding/PropertyValue.cs diff --git a/DynamicData/Binding/SortDirection.cs b/src/DynamicData/Binding/SortDirection.cs similarity index 100% rename from DynamicData/Binding/SortDirection.cs rename to src/DynamicData/Binding/SortDirection.cs diff --git a/DynamicData/Binding/SortExpression.cs b/src/DynamicData/Binding/SortExpression.cs similarity index 100% rename from DynamicData/Binding/SortExpression.cs rename to src/DynamicData/Binding/SortExpression.cs diff --git a/DynamicData/Binding/SortExpressionComparer.cs b/src/DynamicData/Binding/SortExpressionComparer.cs similarity index 100% rename from DynamicData/Binding/SortExpressionComparer.cs rename to src/DynamicData/Binding/SortExpressionComparer.cs diff --git a/DynamicData/Binding/SortedBindingListAdaptor.cs b/src/DynamicData/Binding/SortedBindingListAdaptor.cs similarity index 100% rename from DynamicData/Binding/SortedBindingListAdaptor.cs rename to src/DynamicData/Binding/SortedBindingListAdaptor.cs diff --git a/DynamicData/Binding/SortedObservableCollectionAdaptor.cs b/src/DynamicData/Binding/SortedObservableCollectionAdaptor.cs similarity index 100% rename from DynamicData/Binding/SortedObservableCollectionAdaptor.cs rename to src/DynamicData/Binding/SortedObservableCollectionAdaptor.cs diff --git a/DynamicData/Cache/Change.cs b/src/DynamicData/Cache/Change.cs similarity index 100% rename from DynamicData/Cache/Change.cs rename to src/DynamicData/Cache/Change.cs diff --git a/DynamicData/Cache/ChangeAwareCache.cs b/src/DynamicData/Cache/ChangeAwareCache.cs similarity index 100% rename from DynamicData/Cache/ChangeAwareCache.cs rename to src/DynamicData/Cache/ChangeAwareCache.cs diff --git a/DynamicData/Cache/ChangeReason.cs b/src/DynamicData/Cache/ChangeReason.cs similarity index 100% rename from DynamicData/Cache/ChangeReason.cs rename to src/DynamicData/Cache/ChangeReason.cs diff --git a/DynamicData/Cache/ChangeSet.cs b/src/DynamicData/Cache/ChangeSet.cs similarity index 100% rename from DynamicData/Cache/ChangeSet.cs rename to src/DynamicData/Cache/ChangeSet.cs diff --git a/DynamicData/Cache/DistinctChangeSet.cs b/src/DynamicData/Cache/DistinctChangeSet.cs similarity index 100% rename from DynamicData/Cache/DistinctChangeSet.cs rename to src/DynamicData/Cache/DistinctChangeSet.cs diff --git a/DynamicData/Cache/GroupChangeSet.cs b/src/DynamicData/Cache/GroupChangeSet.cs similarity index 100% rename from DynamicData/Cache/GroupChangeSet.cs rename to src/DynamicData/Cache/GroupChangeSet.cs diff --git a/DynamicData/Cache/ICache.cs b/src/DynamicData/Cache/ICache.cs similarity index 100% rename from DynamicData/Cache/ICache.cs rename to src/DynamicData/Cache/ICache.cs diff --git a/DynamicData/Cache/ICacheUpdater.cs b/src/DynamicData/Cache/ICacheUpdater.cs similarity index 100% rename from DynamicData/Cache/ICacheUpdater.cs rename to src/DynamicData/Cache/ICacheUpdater.cs diff --git a/DynamicData/Cache/IChangeSet.cs b/src/DynamicData/Cache/IChangeSet.cs similarity index 100% rename from DynamicData/Cache/IChangeSet.cs rename to src/DynamicData/Cache/IChangeSet.cs diff --git a/DynamicData/Cache/IChangeSetAdaptor.cs b/src/DynamicData/Cache/IChangeSetAdaptor.cs similarity index 100% rename from DynamicData/Cache/IChangeSetAdaptor.cs rename to src/DynamicData/Cache/IChangeSetAdaptor.cs diff --git a/DynamicData/Cache/IDistinctChangeSet.cs b/src/DynamicData/Cache/IDistinctChangeSet.cs similarity index 100% rename from DynamicData/Cache/IDistinctChangeSet.cs rename to src/DynamicData/Cache/IDistinctChangeSet.cs diff --git a/DynamicData/Cache/IGroup.cs b/src/DynamicData/Cache/IGroup.cs similarity index 100% rename from DynamicData/Cache/IGroup.cs rename to src/DynamicData/Cache/IGroup.cs diff --git a/DynamicData/Cache/IGroupChangeSet.cs b/src/DynamicData/Cache/IGroupChangeSet.cs similarity index 100% rename from DynamicData/Cache/IGroupChangeSet.cs rename to src/DynamicData/Cache/IGroupChangeSet.cs diff --git a/DynamicData/Cache/IGrouping.cs b/src/DynamicData/Cache/IGrouping.cs similarity index 100% rename from DynamicData/Cache/IGrouping.cs rename to src/DynamicData/Cache/IGrouping.cs diff --git a/DynamicData/Cache/IImmutableGroupChangeSet.cs b/src/DynamicData/Cache/IImmutableGroupChangeSet.cs similarity index 100% rename from DynamicData/Cache/IImmutableGroupChangeSet.cs rename to src/DynamicData/Cache/IImmutableGroupChangeSet.cs diff --git a/DynamicData/Cache/IIntermediateCache.cs b/src/DynamicData/Cache/IIntermediateCache.cs similarity index 100% rename from DynamicData/Cache/IIntermediateCache.cs rename to src/DynamicData/Cache/IIntermediateCache.cs diff --git a/DynamicData/Cache/IKeyValue.cs b/src/DynamicData/Cache/IKeyValue.cs similarity index 100% rename from DynamicData/Cache/IKeyValue.cs rename to src/DynamicData/Cache/IKeyValue.cs diff --git a/DynamicData/Cache/IKeyValueCollection.cs b/src/DynamicData/Cache/IKeyValueCollection.cs similarity index 100% rename from DynamicData/Cache/IKeyValueCollection.cs rename to src/DynamicData/Cache/IKeyValueCollection.cs diff --git a/DynamicData/Cache/IObservableCache.cs b/src/DynamicData/Cache/IObservableCache.cs similarity index 100% rename from DynamicData/Cache/IObservableCache.cs rename to src/DynamicData/Cache/IObservableCache.cs diff --git a/DynamicData/Cache/IPageRequest.cs b/src/DynamicData/Cache/IPageRequest.cs similarity index 100% rename from DynamicData/Cache/IPageRequest.cs rename to src/DynamicData/Cache/IPageRequest.cs diff --git a/DynamicData/Cache/IPageResponse.cs b/src/DynamicData/Cache/IPageResponse.cs similarity index 100% rename from DynamicData/Cache/IPageResponse.cs rename to src/DynamicData/Cache/IPageResponse.cs diff --git a/DynamicData/Cache/IPagedChangeSet.cs b/src/DynamicData/Cache/IPagedChangeSet.cs similarity index 100% rename from DynamicData/Cache/IPagedChangeSet.cs rename to src/DynamicData/Cache/IPagedChangeSet.cs diff --git a/DynamicData/Cache/IQuery.cs b/src/DynamicData/Cache/IQuery.cs similarity index 100% rename from DynamicData/Cache/IQuery.cs rename to src/DynamicData/Cache/IQuery.cs diff --git a/DynamicData/Cache/ISortedChangeSet.cs b/src/DynamicData/Cache/ISortedChangeSet.cs similarity index 100% rename from DynamicData/Cache/ISortedChangeSet.cs rename to src/DynamicData/Cache/ISortedChangeSet.cs diff --git a/DynamicData/Cache/ISortedChangeSetAdaptor.cs b/src/DynamicData/Cache/ISortedChangeSetAdaptor.cs similarity index 100% rename from DynamicData/Cache/ISortedChangeSetAdaptor.cs rename to src/DynamicData/Cache/ISortedChangeSetAdaptor.cs diff --git a/DynamicData/Cache/ISourceCache.cs b/src/DynamicData/Cache/ISourceCache.cs similarity index 100% rename from DynamicData/Cache/ISourceCache.cs rename to src/DynamicData/Cache/ISourceCache.cs diff --git a/DynamicData/Cache/ISourceUpdater.cs b/src/DynamicData/Cache/ISourceUpdater.cs similarity index 100% rename from DynamicData/Cache/ISourceUpdater.cs rename to src/DynamicData/Cache/ISourceUpdater.cs diff --git a/DynamicData/Cache/IVirtualChangeSet.cs b/src/DynamicData/Cache/IVirtualChangeSet.cs similarity index 100% rename from DynamicData/Cache/IVirtualChangeSet.cs rename to src/DynamicData/Cache/IVirtualChangeSet.cs diff --git a/DynamicData/Cache/IVirtualParameters.cs b/src/DynamicData/Cache/IVirtualParameters.cs similarity index 100% rename from DynamicData/Cache/IVirtualParameters.cs rename to src/DynamicData/Cache/IVirtualParameters.cs diff --git a/DynamicData/Cache/IVirtualRequest.cs b/src/DynamicData/Cache/IVirtualRequest.cs similarity index 100% rename from DynamicData/Cache/IVirtualRequest.cs rename to src/DynamicData/Cache/IVirtualRequest.cs diff --git a/DynamicData/Cache/IndexedItem.cs b/src/DynamicData/Cache/IndexedItem.cs similarity index 100% rename from DynamicData/Cache/IndexedItem.cs rename to src/DynamicData/Cache/IndexedItem.cs diff --git a/DynamicData/Cache/IntermediateCache.cs b/src/DynamicData/Cache/IntermediateCache.cs similarity index 100% rename from DynamicData/Cache/IntermediateCache.cs rename to src/DynamicData/Cache/IntermediateCache.cs diff --git a/DynamicData/Cache/Internal/AbstractFilter.cs b/src/DynamicData/Cache/Internal/AbstractFilter.cs similarity index 100% rename from DynamicData/Cache/Internal/AbstractFilter.cs rename to src/DynamicData/Cache/Internal/AbstractFilter.cs diff --git a/DynamicData/Cache/Internal/AnonymousObservableCache.cs b/src/DynamicData/Cache/Internal/AnonymousObservableCache.cs similarity index 100% rename from DynamicData/Cache/Internal/AnonymousObservableCache.cs rename to src/DynamicData/Cache/Internal/AnonymousObservableCache.cs diff --git a/DynamicData/Cache/Internal/AnonymousQuery.cs b/src/DynamicData/Cache/Internal/AnonymousQuery.cs similarity index 100% rename from DynamicData/Cache/Internal/AnonymousQuery.cs rename to src/DynamicData/Cache/Internal/AnonymousQuery.cs diff --git a/DynamicData/Cache/Internal/AutoRefresh.cs b/src/DynamicData/Cache/Internal/AutoRefresh.cs similarity index 100% rename from DynamicData/Cache/Internal/AutoRefresh.cs rename to src/DynamicData/Cache/Internal/AutoRefresh.cs diff --git a/DynamicData/Cache/Internal/BatchIf.cs b/src/DynamicData/Cache/Internal/BatchIf.cs similarity index 100% rename from DynamicData/Cache/Internal/BatchIf.cs rename to src/DynamicData/Cache/Internal/BatchIf.cs diff --git a/DynamicData/Cache/Internal/Cache.cs b/src/DynamicData/Cache/Internal/Cache.cs similarity index 100% rename from DynamicData/Cache/Internal/Cache.cs rename to src/DynamicData/Cache/Internal/Cache.cs diff --git a/DynamicData/Cache/Internal/CacheEx.cs b/src/DynamicData/Cache/Internal/CacheEx.cs similarity index 100% rename from DynamicData/Cache/Internal/CacheEx.cs rename to src/DynamicData/Cache/Internal/CacheEx.cs diff --git a/DynamicData/Cache/Internal/CacheUpdater.cs b/src/DynamicData/Cache/Internal/CacheUpdater.cs similarity index 100% rename from DynamicData/Cache/Internal/CacheUpdater.cs rename to src/DynamicData/Cache/Internal/CacheUpdater.cs diff --git a/DynamicData/Cache/Internal/Cast.cs b/src/DynamicData/Cache/Internal/Cast.cs similarity index 100% rename from DynamicData/Cache/Internal/Cast.cs rename to src/DynamicData/Cache/Internal/Cast.cs diff --git a/DynamicData/Cache/Internal/ChangesReducer.cs b/src/DynamicData/Cache/Internal/ChangesReducer.cs similarity index 100% rename from DynamicData/Cache/Internal/ChangesReducer.cs rename to src/DynamicData/Cache/Internal/ChangesReducer.cs diff --git a/DynamicData/Cache/Internal/CombineOperator.cs b/src/DynamicData/Cache/Internal/CombineOperator.cs similarity index 100% rename from DynamicData/Cache/Internal/CombineOperator.cs rename to src/DynamicData/Cache/Internal/CombineOperator.cs diff --git a/DynamicData/Cache/Internal/Combiner.cs b/src/DynamicData/Cache/Internal/Combiner.cs similarity index 100% rename from DynamicData/Cache/Internal/Combiner.cs rename to src/DynamicData/Cache/Internal/Combiner.cs diff --git a/DynamicData/Cache/Internal/DeferUntilLoaded.cs b/src/DynamicData/Cache/Internal/DeferUntilLoaded.cs similarity index 100% rename from DynamicData/Cache/Internal/DeferUntilLoaded.cs rename to src/DynamicData/Cache/Internal/DeferUntilLoaded.cs diff --git a/DynamicData/Cache/Internal/DictionaryExtensions.cs b/src/DynamicData/Cache/Internal/DictionaryExtensions.cs similarity index 100% rename from DynamicData/Cache/Internal/DictionaryExtensions.cs rename to src/DynamicData/Cache/Internal/DictionaryExtensions.cs diff --git a/DynamicData/Cache/Internal/DisposeMany.cs b/src/DynamicData/Cache/Internal/DisposeMany.cs similarity index 100% rename from DynamicData/Cache/Internal/DisposeMany.cs rename to src/DynamicData/Cache/Internal/DisposeMany.cs diff --git a/DynamicData/Cache/Internal/DistinctCalculator.cs b/src/DynamicData/Cache/Internal/DistinctCalculator.cs similarity index 100% rename from DynamicData/Cache/Internal/DistinctCalculator.cs rename to src/DynamicData/Cache/Internal/DistinctCalculator.cs diff --git a/DynamicData/Cache/Internal/DynamicCombiner.cs b/src/DynamicData/Cache/Internal/DynamicCombiner.cs similarity index 100% rename from DynamicData/Cache/Internal/DynamicCombiner.cs rename to src/DynamicData/Cache/Internal/DynamicCombiner.cs diff --git a/DynamicData/Cache/Internal/DynamicFilter.cs b/src/DynamicData/Cache/Internal/DynamicFilter.cs similarity index 100% rename from DynamicData/Cache/Internal/DynamicFilter.cs rename to src/DynamicData/Cache/Internal/DynamicFilter.cs diff --git a/DynamicData/Cache/Internal/EditDiff.cs b/src/DynamicData/Cache/Internal/EditDiff.cs similarity index 100% rename from DynamicData/Cache/Internal/EditDiff.cs rename to src/DynamicData/Cache/Internal/EditDiff.cs diff --git a/DynamicData/Cache/Internal/ExpirableItem.cs b/src/DynamicData/Cache/Internal/ExpirableItem.cs similarity index 100% rename from DynamicData/Cache/Internal/ExpirableItem.cs rename to src/DynamicData/Cache/Internal/ExpirableItem.cs diff --git a/DynamicData/Cache/Internal/FilterEx.cs b/src/DynamicData/Cache/Internal/FilterEx.cs similarity index 100% rename from DynamicData/Cache/Internal/FilterEx.cs rename to src/DynamicData/Cache/Internal/FilterEx.cs diff --git a/DynamicData/Cache/Internal/FilterOnProperty.cs b/src/DynamicData/Cache/Internal/FilterOnProperty.cs similarity index 100% rename from DynamicData/Cache/Internal/FilterOnProperty.cs rename to src/DynamicData/Cache/Internal/FilterOnProperty.cs diff --git a/DynamicData/Cache/Internal/FilteredIndexCalculator.cs b/src/DynamicData/Cache/Internal/FilteredIndexCalculator.cs similarity index 100% rename from DynamicData/Cache/Internal/FilteredIndexCalculator.cs rename to src/DynamicData/Cache/Internal/FilteredIndexCalculator.cs diff --git a/DynamicData/Cache/Internal/FinallySafe.cs b/src/DynamicData/Cache/Internal/FinallySafe.cs similarity index 100% rename from DynamicData/Cache/Internal/FinallySafe.cs rename to src/DynamicData/Cache/Internal/FinallySafe.cs diff --git a/DynamicData/Cache/Internal/FullJoin.cs b/src/DynamicData/Cache/Internal/FullJoin.cs similarity index 100% rename from DynamicData/Cache/Internal/FullJoin.cs rename to src/DynamicData/Cache/Internal/FullJoin.cs diff --git a/DynamicData/Cache/Internal/FullJoinMany.cs b/src/DynamicData/Cache/Internal/FullJoinMany.cs similarity index 100% rename from DynamicData/Cache/Internal/FullJoinMany.cs rename to src/DynamicData/Cache/Internal/FullJoinMany.cs diff --git a/DynamicData/Cache/Internal/GroupImmutable.cs b/src/DynamicData/Cache/Internal/GroupImmutable.cs similarity index 100% rename from DynamicData/Cache/Internal/GroupImmutable.cs rename to src/DynamicData/Cache/Internal/GroupImmutable.cs diff --git a/DynamicData/Cache/Internal/GroupOn.cs b/src/DynamicData/Cache/Internal/GroupOn.cs similarity index 100% rename from DynamicData/Cache/Internal/GroupOn.cs rename to src/DynamicData/Cache/Internal/GroupOn.cs diff --git a/DynamicData/Cache/Internal/GroupOnProperty.cs b/src/DynamicData/Cache/Internal/GroupOnProperty.cs similarity index 100% rename from DynamicData/Cache/Internal/GroupOnProperty.cs rename to src/DynamicData/Cache/Internal/GroupOnProperty.cs diff --git a/DynamicData/Cache/Internal/GroupOnPropertyWithImmutableState.cs b/src/DynamicData/Cache/Internal/GroupOnPropertyWithImmutableState.cs similarity index 100% rename from DynamicData/Cache/Internal/GroupOnPropertyWithImmutableState.cs rename to src/DynamicData/Cache/Internal/GroupOnPropertyWithImmutableState.cs diff --git a/DynamicData/Cache/Internal/IFilter.cs b/src/DynamicData/Cache/Internal/IFilter.cs similarity index 100% rename from DynamicData/Cache/Internal/IFilter.cs rename to src/DynamicData/Cache/Internal/IFilter.cs diff --git a/DynamicData/Cache/Internal/IKeySelector.cs b/src/DynamicData/Cache/Internal/IKeySelector.cs similarity index 100% rename from DynamicData/Cache/Internal/IKeySelector.cs rename to src/DynamicData/Cache/Internal/IKeySelector.cs diff --git a/DynamicData/Cache/Internal/ImmutableGroup.cs b/src/DynamicData/Cache/Internal/ImmutableGroup.cs similarity index 100% rename from DynamicData/Cache/Internal/ImmutableGroup.cs rename to src/DynamicData/Cache/Internal/ImmutableGroup.cs diff --git a/DynamicData/Cache/Internal/ImmutableGroupChangeSet.cs b/src/DynamicData/Cache/Internal/ImmutableGroupChangeSet.cs similarity index 100% rename from DynamicData/Cache/Internal/ImmutableGroupChangeSet.cs rename to src/DynamicData/Cache/Internal/ImmutableGroupChangeSet.cs diff --git a/DynamicData/Cache/Internal/IndexAndNode.cs b/src/DynamicData/Cache/Internal/IndexAndNode.cs similarity index 100% rename from DynamicData/Cache/Internal/IndexAndNode.cs rename to src/DynamicData/Cache/Internal/IndexAndNode.cs diff --git a/DynamicData/Cache/Internal/IndexCalculator.cs b/src/DynamicData/Cache/Internal/IndexCalculator.cs similarity index 100% rename from DynamicData/Cache/Internal/IndexCalculator.cs rename to src/DynamicData/Cache/Internal/IndexCalculator.cs diff --git a/DynamicData/Cache/Internal/InnerJoin.cs b/src/DynamicData/Cache/Internal/InnerJoin.cs similarity index 100% rename from DynamicData/Cache/Internal/InnerJoin.cs rename to src/DynamicData/Cache/Internal/InnerJoin.cs diff --git a/DynamicData/Cache/Internal/InnerJoinMany.cs b/src/DynamicData/Cache/Internal/InnerJoinMany.cs similarity index 100% rename from DynamicData/Cache/Internal/InnerJoinMany.cs rename to src/DynamicData/Cache/Internal/InnerJoinMany.cs diff --git a/DynamicData/Cache/Internal/KeyComparer.cs b/src/DynamicData/Cache/Internal/KeyComparer.cs similarity index 100% rename from DynamicData/Cache/Internal/KeyComparer.cs rename to src/DynamicData/Cache/Internal/KeyComparer.cs diff --git a/DynamicData/Cache/Internal/KeySelector.cs b/src/DynamicData/Cache/Internal/KeySelector.cs similarity index 100% rename from DynamicData/Cache/Internal/KeySelector.cs rename to src/DynamicData/Cache/Internal/KeySelector.cs diff --git a/DynamicData/Cache/Internal/KeySelectorException.cs b/src/DynamicData/Cache/Internal/KeySelectorException.cs similarity index 100% rename from DynamicData/Cache/Internal/KeySelectorException.cs rename to src/DynamicData/Cache/Internal/KeySelectorException.cs diff --git a/DynamicData/Cache/Internal/KeyValueCollection.cs b/src/DynamicData/Cache/Internal/KeyValueCollection.cs similarity index 100% rename from DynamicData/Cache/Internal/KeyValueCollection.cs rename to src/DynamicData/Cache/Internal/KeyValueCollection.cs diff --git a/DynamicData/Cache/Internal/KeyValueComparer.cs b/src/DynamicData/Cache/Internal/KeyValueComparer.cs similarity index 100% rename from DynamicData/Cache/Internal/KeyValueComparer.cs rename to src/DynamicData/Cache/Internal/KeyValueComparer.cs diff --git a/DynamicData/Cache/Internal/LeftJoin.cs b/src/DynamicData/Cache/Internal/LeftJoin.cs similarity index 100% rename from DynamicData/Cache/Internal/LeftJoin.cs rename to src/DynamicData/Cache/Internal/LeftJoin.cs diff --git a/DynamicData/Cache/Internal/LeftJoinMany.cs b/src/DynamicData/Cache/Internal/LeftJoinMany.cs similarity index 100% rename from DynamicData/Cache/Internal/LeftJoinMany.cs rename to src/DynamicData/Cache/Internal/LeftJoinMany.cs diff --git a/DynamicData/Cache/Internal/LockFreeObservableCache.cs b/src/DynamicData/Cache/Internal/LockFreeObservableCache.cs similarity index 100% rename from DynamicData/Cache/Internal/LockFreeObservableCache.cs rename to src/DynamicData/Cache/Internal/LockFreeObservableCache.cs diff --git a/DynamicData/Cache/Internal/ManagedGroup.cs b/src/DynamicData/Cache/Internal/ManagedGroup.cs similarity index 100% rename from DynamicData/Cache/Internal/ManagedGroup.cs rename to src/DynamicData/Cache/Internal/ManagedGroup.cs diff --git a/DynamicData/Cache/Internal/MergeMany.cs b/src/DynamicData/Cache/Internal/MergeMany.cs similarity index 100% rename from DynamicData/Cache/Internal/MergeMany.cs rename to src/DynamicData/Cache/Internal/MergeMany.cs diff --git a/DynamicData/Cache/Internal/MergeManyItems.cs b/src/DynamicData/Cache/Internal/MergeManyItems.cs similarity index 100% rename from DynamicData/Cache/Internal/MergeManyItems.cs rename to src/DynamicData/Cache/Internal/MergeManyItems.cs diff --git a/DynamicData/Cache/Internal/ObservableWithValue.cs b/src/DynamicData/Cache/Internal/ObservableWithValue.cs similarity index 100% rename from DynamicData/Cache/Internal/ObservableWithValue.cs rename to src/DynamicData/Cache/Internal/ObservableWithValue.cs diff --git a/DynamicData/Cache/Internal/Page.cs b/src/DynamicData/Cache/Internal/Page.cs similarity index 100% rename from DynamicData/Cache/Internal/Page.cs rename to src/DynamicData/Cache/Internal/Page.cs diff --git a/DynamicData/Cache/Internal/QueryWhenChanged.cs b/src/DynamicData/Cache/Internal/QueryWhenChanged.cs similarity index 100% rename from DynamicData/Cache/Internal/QueryWhenChanged.cs rename to src/DynamicData/Cache/Internal/QueryWhenChanged.cs diff --git a/DynamicData/Cache/Internal/ReaderWriter.cs b/src/DynamicData/Cache/Internal/ReaderWriter.cs similarity index 100% rename from DynamicData/Cache/Internal/ReaderWriter.cs rename to src/DynamicData/Cache/Internal/ReaderWriter.cs diff --git a/DynamicData/Cache/Internal/RefCount.cs b/src/DynamicData/Cache/Internal/RefCount.cs similarity index 100% rename from DynamicData/Cache/Internal/RefCount.cs rename to src/DynamicData/Cache/Internal/RefCount.cs diff --git a/DynamicData/Cache/Internal/RemoveKeyEnumerator.cs b/src/DynamicData/Cache/Internal/RemoveKeyEnumerator.cs similarity index 100% rename from DynamicData/Cache/Internal/RemoveKeyEnumerator.cs rename to src/DynamicData/Cache/Internal/RemoveKeyEnumerator.cs diff --git a/DynamicData/Cache/Internal/RightJoin.cs b/src/DynamicData/Cache/Internal/RightJoin.cs similarity index 100% rename from DynamicData/Cache/Internal/RightJoin.cs rename to src/DynamicData/Cache/Internal/RightJoin.cs diff --git a/DynamicData/Cache/Internal/RightJoinMany.cs b/src/DynamicData/Cache/Internal/RightJoinMany.cs similarity index 100% rename from DynamicData/Cache/Internal/RightJoinMany.cs rename to src/DynamicData/Cache/Internal/RightJoinMany.cs diff --git a/DynamicData/Cache/Internal/SizeExpirer.cs b/src/DynamicData/Cache/Internal/SizeExpirer.cs similarity index 100% rename from DynamicData/Cache/Internal/SizeExpirer.cs rename to src/DynamicData/Cache/Internal/SizeExpirer.cs diff --git a/DynamicData/Cache/Internal/SizeLimiter.cs b/src/DynamicData/Cache/Internal/SizeLimiter.cs similarity index 100% rename from DynamicData/Cache/Internal/SizeLimiter.cs rename to src/DynamicData/Cache/Internal/SizeLimiter.cs diff --git a/DynamicData/Cache/Internal/Sort.cs b/src/DynamicData/Cache/Internal/Sort.cs similarity index 100% rename from DynamicData/Cache/Internal/Sort.cs rename to src/DynamicData/Cache/Internal/Sort.cs diff --git a/DynamicData/Cache/Internal/SpecifiedGrouper.cs b/src/DynamicData/Cache/Internal/SpecifiedGrouper.cs similarity index 100% rename from DynamicData/Cache/Internal/SpecifiedGrouper.cs rename to src/DynamicData/Cache/Internal/SpecifiedGrouper.cs diff --git a/DynamicData/Cache/Internal/StaticFilter.cs b/src/DynamicData/Cache/Internal/StaticFilter.cs similarity index 100% rename from DynamicData/Cache/Internal/StaticFilter.cs rename to src/DynamicData/Cache/Internal/StaticFilter.cs diff --git a/DynamicData/Cache/Internal/StatusMonitor.cs b/src/DynamicData/Cache/Internal/StatusMonitor.cs similarity index 100% rename from DynamicData/Cache/Internal/StatusMonitor.cs rename to src/DynamicData/Cache/Internal/StatusMonitor.cs diff --git a/DynamicData/Cache/Internal/SubscribeMany.cs b/src/DynamicData/Cache/Internal/SubscribeMany.cs similarity index 100% rename from DynamicData/Cache/Internal/SubscribeMany.cs rename to src/DynamicData/Cache/Internal/SubscribeMany.cs diff --git a/DynamicData/Cache/Internal/Switch.cs b/src/DynamicData/Cache/Internal/Switch.cs similarity index 100% rename from DynamicData/Cache/Internal/Switch.cs rename to src/DynamicData/Cache/Internal/Switch.cs diff --git a/DynamicData/Cache/Internal/TimeExpirer.cs b/src/DynamicData/Cache/Internal/TimeExpirer.cs similarity index 100% rename from DynamicData/Cache/Internal/TimeExpirer.cs rename to src/DynamicData/Cache/Internal/TimeExpirer.cs diff --git a/DynamicData/Cache/Internal/ToObservableChangeSet.cs b/src/DynamicData/Cache/Internal/ToObservableChangeSet.cs similarity index 100% rename from DynamicData/Cache/Internal/ToObservableChangeSet.cs rename to src/DynamicData/Cache/Internal/ToObservableChangeSet.cs diff --git a/DynamicData/Cache/Internal/Transform.cs b/src/DynamicData/Cache/Internal/Transform.cs similarity index 100% rename from DynamicData/Cache/Internal/Transform.cs rename to src/DynamicData/Cache/Internal/Transform.cs diff --git a/DynamicData/Cache/Internal/TransformAsync.cs b/src/DynamicData/Cache/Internal/TransformAsync.cs similarity index 100% rename from DynamicData/Cache/Internal/TransformAsync.cs rename to src/DynamicData/Cache/Internal/TransformAsync.cs diff --git a/DynamicData/Cache/Internal/TransformMany.cs b/src/DynamicData/Cache/Internal/TransformMany.cs similarity index 100% rename from DynamicData/Cache/Internal/TransformMany.cs rename to src/DynamicData/Cache/Internal/TransformMany.cs diff --git a/DynamicData/Cache/Internal/TransformWithForcedTransform.cs b/src/DynamicData/Cache/Internal/TransformWithForcedTransform.cs similarity index 100% rename from DynamicData/Cache/Internal/TransformWithForcedTransform.cs rename to src/DynamicData/Cache/Internal/TransformWithForcedTransform.cs diff --git a/DynamicData/Cache/Internal/TreeBuilder.cs b/src/DynamicData/Cache/Internal/TreeBuilder.cs similarity index 100% rename from DynamicData/Cache/Internal/TreeBuilder.cs rename to src/DynamicData/Cache/Internal/TreeBuilder.cs diff --git a/DynamicData/Cache/Internal/TrueFor.cs b/src/DynamicData/Cache/Internal/TrueFor.cs similarity index 100% rename from DynamicData/Cache/Internal/TrueFor.cs rename to src/DynamicData/Cache/Internal/TrueFor.cs diff --git a/DynamicData/Cache/Internal/Virtualiser.cs b/src/DynamicData/Cache/Internal/Virtualiser.cs similarity index 100% rename from DynamicData/Cache/Internal/Virtualiser.cs rename to src/DynamicData/Cache/Internal/Virtualiser.cs diff --git a/DynamicData/Cache/MissingKeyException.cs b/src/DynamicData/Cache/MissingKeyException.cs similarity index 100% rename from DynamicData/Cache/MissingKeyException.cs rename to src/DynamicData/Cache/MissingKeyException.cs diff --git a/DynamicData/Cache/Node.cs b/src/DynamicData/Cache/Node.cs similarity index 97% rename from DynamicData/Cache/Node.cs rename to src/DynamicData/Cache/Node.cs index 69ec8fc2b..6f49b980a 100644 --- a/DynamicData/Cache/Node.cs +++ b/src/DynamicData/Cache/Node.cs @@ -1,171 +1,171 @@ -using System; -using System.Collections.Generic; -using System.Reactive.Disposables; -using DynamicData.Annotations; -using DynamicData.Kernel; -// ReSharper disable once CheckNamespace -namespace DynamicData -{ - /// - /// Node describing the relationship between and item and it's ancestors and descendent - /// - /// The type of the object. - /// The type of the key. - public class Node : IDisposable, IEquatable> - where TObject : class - { - private readonly ISourceCache, TKey> _children = new SourceCache, TKey>(n => n.Key); - private readonly IDisposable _cleanUp; - - /// - /// Initializes a new instance of the class. - /// - /// The item. - /// The key. - public Node(TObject item, TKey key) - : this(item, key, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The item. - /// The key. - /// The parent. - /// - public Node([NotNull] TObject item, TKey key, Optional> parent) - { - Item = item ?? throw new ArgumentNullException(nameof(item)); - Key = key; - Parent = parent; - Children = _children.AsObservableCache(); - _cleanUp = new CompositeDisposable(Children, _children); - } - - /// - /// The item - /// - public TObject Item { get; } - - /// - /// The key - /// - public TKey Key { get; } - - /// - /// Gets the parent if it has one - /// - public Optional> Parent { get; internal set; } - - /// - /// The child nodes - /// - public IObservableCache, TKey> Children { get; } - - /// - /// Gets or sets a value indicating whether this instance is root. - /// - /// - /// true if this instance is root node; otherwise, false. - /// - public bool IsRoot => !Parent.HasValue; - - /// - /// Gets the depth i.e. how many degrees of separation from the parent - /// - public int Depth - { - get - { - var i = 0; - var parent = Parent; - do - { - if (!parent.HasValue) - break; - i++; - parent = parent.Value.Parent; - } while (true); - return i; - } - } - - internal void Update(Action, TKey>> updateAction) - { - _children.Edit(updateAction); - } - - #region Equality - - - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. - /// The object to compare with the current object. - /// 2 - public bool Equals(Node other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return EqualityComparer.Default.Equals(Key, other.Key); - } - - /// Determines whether the specified object is equal to the current object. - /// true if the specified object is equal to the current object; otherwise, false. - /// The object to compare with the current object. - /// 2 - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((Node)obj); - } - - /// Serves as the default hash function. - /// A hash code for the current object. - /// 2 - public override int GetHashCode() - { - return EqualityComparer.Default.GetHashCode(Key); - } - - /// - /// Determines whether the specified objects are equal - /// - public static bool operator ==(Node left, Node right) - { - return Equals(left, right); - } - - /// - /// Determines whether the specified objects are not equal - /// - public static bool operator !=(Node left, Node right) - { - return !Equals(left, right); - } - - #endregion - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - var count = Children.Count == 0 ? "" : $" ({Children.Count} children)"; - return $"{Item}{count}"; - } - - - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// 2 - public void Dispose() - { - _cleanUp.Dispose(); - } - } -} +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; +using DynamicData.Annotations; +using DynamicData.Kernel; +// ReSharper disable once CheckNamespace +namespace DynamicData +{ + /// + /// Node describing the relationship between and item and it's ancestors and descendent + /// + /// The type of the object. + /// The type of the key. + public class Node : IDisposable, IEquatable> + where TObject : class + { + private readonly ISourceCache, TKey> _children = new SourceCache, TKey>(n => n.Key); + private readonly IDisposable _cleanUp; + + /// + /// Initializes a new instance of the class. + /// + /// The item. + /// The key. + public Node(TObject item, TKey key) + : this(item, key, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The item. + /// The key. + /// The parent. + /// + public Node([NotNull] TObject item, TKey key, Optional> parent) + { + Item = item ?? throw new ArgumentNullException(nameof(item)); + Key = key; + Parent = parent; + Children = _children.AsObservableCache(); + _cleanUp = new CompositeDisposable(Children, _children); + } + + /// + /// The item + /// + public TObject Item { get; } + + /// + /// The key + /// + public TKey Key { get; } + + /// + /// Gets the parent if it has one + /// + public Optional> Parent { get; internal set; } + + /// + /// The child nodes + /// + public IObservableCache, TKey> Children { get; } + + /// + /// Gets or sets a value indicating whether this instance is root. + /// + /// + /// true if this instance is root node; otherwise, false. + /// + public bool IsRoot => !Parent.HasValue; + + /// + /// Gets the depth i.e. how many degrees of separation from the parent + /// + public int Depth + { + get + { + var i = 0; + var parent = Parent; + do + { + if (!parent.HasValue) + break; + i++; + parent = parent.Value.Parent; + } while (true); + return i; + } + } + + internal void Update(Action, TKey>> updateAction) + { + _children.Edit(updateAction); + } + + #region Equality + + + /// Determines whether the specified object is equal to the current object. + /// true if the specified object is equal to the current object; otherwise, false. + /// The object to compare with the current object. + /// 2 + public bool Equals(Node other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return EqualityComparer.Default.Equals(Key, other.Key); + } + + /// Determines whether the specified object is equal to the current object. + /// true if the specified object is equal to the current object; otherwise, false. + /// The object to compare with the current object. + /// 2 + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Node)obj); + } + + /// Serves as the default hash function. + /// A hash code for the current object. + /// 2 + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(Key); + } + + /// + /// Determines whether the specified objects are equal + /// + public static bool operator ==(Node left, Node right) + { + return Equals(left, right); + } + + /// + /// Determines whether the specified objects are not equal + /// + public static bool operator !=(Node left, Node right) + { + return !Equals(left, right); + } + + #endregion + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + var count = Children.Count == 0 ? "" : $" ({Children.Count} children)"; + return $"{Item}{count}"; + } + + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// 2 + public void Dispose() + { + _cleanUp.Dispose(); + } + } +} diff --git a/DynamicData/Cache/ObservableCache.cs b/src/DynamicData/Cache/ObservableCache.cs similarity index 100% rename from DynamicData/Cache/ObservableCache.cs rename to src/DynamicData/Cache/ObservableCache.cs diff --git a/DynamicData/Cache/ObservableCacheEx.cs b/src/DynamicData/Cache/ObservableCacheEx.cs similarity index 100% rename from DynamicData/Cache/ObservableCacheEx.cs rename to src/DynamicData/Cache/ObservableCacheEx.cs diff --git a/DynamicData/Cache/PageRequest.cs b/src/DynamicData/Cache/PageRequest.cs similarity index 100% rename from DynamicData/Cache/PageRequest.cs rename to src/DynamicData/Cache/PageRequest.cs diff --git a/DynamicData/Cache/PageResponse.cs b/src/DynamicData/Cache/PageResponse.cs similarity index 100% rename from DynamicData/Cache/PageResponse.cs rename to src/DynamicData/Cache/PageResponse.cs diff --git a/DynamicData/Cache/PagedChangeSet.cs b/src/DynamicData/Cache/PagedChangeSet.cs similarity index 100% rename from DynamicData/Cache/PagedChangeSet.cs rename to src/DynamicData/Cache/PagedChangeSet.cs diff --git a/DynamicData/Cache/SortOptimisations.cs b/src/DynamicData/Cache/SortOptimisations.cs similarity index 100% rename from DynamicData/Cache/SortOptimisations.cs rename to src/DynamicData/Cache/SortOptimisations.cs diff --git a/DynamicData/Cache/SortReason.cs b/src/DynamicData/Cache/SortReason.cs similarity index 100% rename from DynamicData/Cache/SortReason.cs rename to src/DynamicData/Cache/SortReason.cs diff --git a/DynamicData/Cache/SortedChangeSet.cs b/src/DynamicData/Cache/SortedChangeSet.cs similarity index 100% rename from DynamicData/Cache/SortedChangeSet.cs rename to src/DynamicData/Cache/SortedChangeSet.cs diff --git a/DynamicData/Cache/SourceCache.cs b/src/DynamicData/Cache/SourceCache.cs similarity index 97% rename from DynamicData/Cache/SourceCache.cs rename to src/DynamicData/Cache/SourceCache.cs index 5ec6e06fe..09a82167d 100644 --- a/DynamicData/Cache/SourceCache.cs +++ b/src/DynamicData/Cache/SourceCache.cs @@ -1,64 +1,64 @@ -using System; -using System.Collections.Generic; -using DynamicData.Kernel; -// ReSharper disable once CheckNamespace -namespace DynamicData -{ - /// - /// An observable cache which exposes an update API. Used at the root - /// of all observable chains - /// - /// The type of the object. - /// The type of the key. - public class SourceCache : ISourceCache - { - private readonly ObservableCache _innerCache; - - /// - /// Initializes a new instance of the class. - /// - /// The key selector. - /// keySelector - public SourceCache(Func keySelector) - { - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - _innerCache = new ObservableCache(keySelector); - } - - #region Delegated Members - - /// - public void Edit(Action> updateAction) => _innerCache.UpdateFromSource(updateAction); - - /// - public IObservable CountChanged => _innerCache.CountChanged; - - /// - public IObservable> Connect(Func predicate = null) => _innerCache.Connect(predicate); - /// - public IObservable> Preview(Func predicate = null) => _innerCache.Preview(predicate); - - /// - public IObservable> Watch(TKey key) => _innerCache.Watch(key); - - /// - public int Count => _innerCache.Count; - - /// - public IEnumerable Items => _innerCache.Items; - - /// - public IEnumerable> KeyValues => _innerCache.KeyValues; - - /// - public IEnumerable Keys => _innerCache.Keys; - - /// - public Optional Lookup(TKey key) => _innerCache.Lookup(key); - - /// - public void Dispose() => _innerCache.Dispose(); - - #endregion - } -} +using System; +using System.Collections.Generic; +using DynamicData.Kernel; +// ReSharper disable once CheckNamespace +namespace DynamicData +{ + /// + /// An observable cache which exposes an update API. Used at the root + /// of all observable chains + /// + /// The type of the object. + /// The type of the key. + public class SourceCache : ISourceCache + { + private readonly ObservableCache _innerCache; + + /// + /// Initializes a new instance of the class. + /// + /// The key selector. + /// keySelector + public SourceCache(Func keySelector) + { + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + _innerCache = new ObservableCache(keySelector); + } + + #region Delegated Members + + /// + public void Edit(Action> updateAction) => _innerCache.UpdateFromSource(updateAction); + + /// + public IObservable CountChanged => _innerCache.CountChanged; + + /// + public IObservable> Connect(Func predicate = null) => _innerCache.Connect(predicate); + /// + public IObservable> Preview(Func predicate = null) => _innerCache.Preview(predicate); + + /// + public IObservable> Watch(TKey key) => _innerCache.Watch(key); + + /// + public int Count => _innerCache.Count; + + /// + public IEnumerable Items => _innerCache.Items; + + /// + public IEnumerable> KeyValues => _innerCache.KeyValues; + + /// + public IEnumerable Keys => _innerCache.Keys; + + /// + public Optional Lookup(TKey key) => _innerCache.Lookup(key); + + /// + public void Dispose() => _innerCache.Dispose(); + + #endregion + } +} diff --git a/DynamicData/Cache/SourceCacheEx.cs b/src/DynamicData/Cache/SourceCacheEx.cs similarity index 100% rename from DynamicData/Cache/SourceCacheEx.cs rename to src/DynamicData/Cache/SourceCacheEx.cs diff --git a/DynamicData/Cache/Tests/ChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/ChangeSetAggregator.cs similarity index 100% rename from DynamicData/Cache/Tests/ChangeSetAggregator.cs rename to src/DynamicData/Cache/Tests/ChangeSetAggregator.cs diff --git a/DynamicData/Cache/Tests/DistinctChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/DistinctChangeSetAggregator.cs similarity index 100% rename from DynamicData/Cache/Tests/DistinctChangeSetAggregator.cs rename to src/DynamicData/Cache/Tests/DistinctChangeSetAggregator.cs diff --git a/DynamicData/Cache/Tests/PagedChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/PagedChangeSetAggregator.cs similarity index 100% rename from DynamicData/Cache/Tests/PagedChangeSetAggregator.cs rename to src/DynamicData/Cache/Tests/PagedChangeSetAggregator.cs diff --git a/DynamicData/Cache/Tests/SortedChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/SortedChangeSetAggregator.cs similarity index 100% rename from DynamicData/Cache/Tests/SortedChangeSetAggregator.cs rename to src/DynamicData/Cache/Tests/SortedChangeSetAggregator.cs diff --git a/DynamicData/Cache/Tests/TestEx.cs b/src/DynamicData/Cache/Tests/TestEx.cs similarity index 100% rename from DynamicData/Cache/Tests/TestEx.cs rename to src/DynamicData/Cache/Tests/TestEx.cs diff --git a/DynamicData/Cache/Tests/VirtualChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/VirtualChangeSetAggregator.cs similarity index 100% rename from DynamicData/Cache/Tests/VirtualChangeSetAggregator.cs rename to src/DynamicData/Cache/Tests/VirtualChangeSetAggregator.cs diff --git a/DynamicData/Cache/VirtualChangeSet.cs b/src/DynamicData/Cache/VirtualChangeSet.cs similarity index 100% rename from DynamicData/Cache/VirtualChangeSet.cs rename to src/DynamicData/Cache/VirtualChangeSet.cs diff --git a/DynamicData/Cache/VirtualRequest.cs b/src/DynamicData/Cache/VirtualRequest.cs similarity index 100% rename from DynamicData/Cache/VirtualRequest.cs rename to src/DynamicData/Cache/VirtualRequest.cs diff --git a/DynamicData/Cache/VirtualResponse.cs b/src/DynamicData/Cache/VirtualResponse.cs similarity index 100% rename from DynamicData/Cache/VirtualResponse.cs rename to src/DynamicData/Cache/VirtualResponse.cs diff --git a/DynamicData/Diagnostics/ChangeStatistics.cs b/src/DynamicData/Diagnostics/ChangeStatistics.cs similarity index 100% rename from DynamicData/Diagnostics/ChangeStatistics.cs rename to src/DynamicData/Diagnostics/ChangeStatistics.cs diff --git a/DynamicData/Diagnostics/ChangeSummary.cs b/src/DynamicData/Diagnostics/ChangeSummary.cs similarity index 100% rename from DynamicData/Diagnostics/ChangeSummary.cs rename to src/DynamicData/Diagnostics/ChangeSummary.cs diff --git a/DynamicData/Diagnostics/DiagnosticOperators.cs b/src/DynamicData/Diagnostics/DiagnosticOperators.cs similarity index 100% rename from DynamicData/Diagnostics/DiagnosticOperators.cs rename to src/DynamicData/Diagnostics/DiagnosticOperators.cs diff --git a/DynamicData/DynamicData.csproj b/src/DynamicData/DynamicData.csproj similarity index 100% rename from DynamicData/DynamicData.csproj rename to src/DynamicData/DynamicData.csproj diff --git a/DynamicData/DynamicData.csproj.DotSettings b/src/DynamicData/DynamicData.csproj.DotSettings similarity index 100% rename from DynamicData/DynamicData.csproj.DotSettings rename to src/DynamicData/DynamicData.csproj.DotSettings diff --git a/DynamicData/EnumerableEx.cs b/src/DynamicData/EnumerableEx.cs similarity index 100% rename from DynamicData/EnumerableEx.cs rename to src/DynamicData/EnumerableEx.cs diff --git a/DynamicData/Experimental/ExperimentalEx.cs b/src/DynamicData/Experimental/ExperimentalEx.cs similarity index 100% rename from DynamicData/Experimental/ExperimentalEx.cs rename to src/DynamicData/Experimental/ExperimentalEx.cs diff --git a/DynamicData/Experimental/ISubjectWithRefCount.cs b/src/DynamicData/Experimental/ISubjectWithRefCount.cs similarity index 100% rename from DynamicData/Experimental/ISubjectWithRefCount.cs rename to src/DynamicData/Experimental/ISubjectWithRefCount.cs diff --git a/DynamicData/Experimental/IWatcher.cs b/src/DynamicData/Experimental/IWatcher.cs similarity index 100% rename from DynamicData/Experimental/IWatcher.cs rename to src/DynamicData/Experimental/IWatcher.cs diff --git a/DynamicData/Experimental/SubjectWithRefCount.cs b/src/DynamicData/Experimental/SubjectWithRefCount.cs similarity index 100% rename from DynamicData/Experimental/SubjectWithRefCount.cs rename to src/DynamicData/Experimental/SubjectWithRefCount.cs diff --git a/DynamicData/Experimental/Watcher.cs b/src/DynamicData/Experimental/Watcher.cs similarity index 100% rename from DynamicData/Experimental/Watcher.cs rename to src/DynamicData/Experimental/Watcher.cs diff --git a/DynamicData/IChangeSet.cs b/src/DynamicData/IChangeSet.cs similarity index 100% rename from DynamicData/IChangeSet.cs rename to src/DynamicData/IChangeSet.cs diff --git a/DynamicData/Kernel/ConnectionStatus.cs b/src/DynamicData/Kernel/ConnectionStatus.cs similarity index 100% rename from DynamicData/Kernel/ConnectionStatus.cs rename to src/DynamicData/Kernel/ConnectionStatus.cs diff --git a/DynamicData/Kernel/DoubleCheck.cs b/src/DynamicData/Kernel/DoubleCheck.cs similarity index 100% rename from DynamicData/Kernel/DoubleCheck.cs rename to src/DynamicData/Kernel/DoubleCheck.cs diff --git a/DynamicData/Kernel/EnumerableEx.cs b/src/DynamicData/Kernel/EnumerableEx.cs similarity index 100% rename from DynamicData/Kernel/EnumerableEx.cs rename to src/DynamicData/Kernel/EnumerableEx.cs diff --git a/DynamicData/Kernel/EnumerableIList.cs b/src/DynamicData/Kernel/EnumerableIList.cs similarity index 100% rename from DynamicData/Kernel/EnumerableIList.cs rename to src/DynamicData/Kernel/EnumerableIList.cs diff --git a/DynamicData/Kernel/Error.cs b/src/DynamicData/Kernel/Error.cs similarity index 100% rename from DynamicData/Kernel/Error.cs rename to src/DynamicData/Kernel/Error.cs diff --git a/DynamicData/Kernel/ISupportsCapcity.cs b/src/DynamicData/Kernel/ISupportsCapcity.cs similarity index 100% rename from DynamicData/Kernel/ISupportsCapcity.cs rename to src/DynamicData/Kernel/ISupportsCapcity.cs diff --git a/DynamicData/Kernel/InternalEx.cs b/src/DynamicData/Kernel/InternalEx.cs similarity index 100% rename from DynamicData/Kernel/InternalEx.cs rename to src/DynamicData/Kernel/InternalEx.cs diff --git a/DynamicData/Kernel/ItemWithIndex.cs b/src/DynamicData/Kernel/ItemWithIndex.cs similarity index 100% rename from DynamicData/Kernel/ItemWithIndex.cs rename to src/DynamicData/Kernel/ItemWithIndex.cs diff --git a/DynamicData/Kernel/ItemWithValue.cs b/src/DynamicData/Kernel/ItemWithValue.cs similarity index 100% rename from DynamicData/Kernel/ItemWithValue.cs rename to src/DynamicData/Kernel/ItemWithValue.cs diff --git a/DynamicData/Kernel/OptionElse.cs b/src/DynamicData/Kernel/OptionElse.cs similarity index 100% rename from DynamicData/Kernel/OptionElse.cs rename to src/DynamicData/Kernel/OptionElse.cs diff --git a/DynamicData/Kernel/OptionExtensions.cs b/src/DynamicData/Kernel/OptionExtensions.cs similarity index 100% rename from DynamicData/Kernel/OptionExtensions.cs rename to src/DynamicData/Kernel/OptionExtensions.cs diff --git a/DynamicData/Kernel/Optional.cs b/src/DynamicData/Kernel/Optional.cs similarity index 100% rename from DynamicData/Kernel/Optional.cs rename to src/DynamicData/Kernel/Optional.cs diff --git a/DynamicData/Kernel/ParallelEx.cs b/src/DynamicData/Kernel/ParallelEx.cs similarity index 100% rename from DynamicData/Kernel/ParallelEx.cs rename to src/DynamicData/Kernel/ParallelEx.cs diff --git a/DynamicData/Kernel/ReadOnlyCollectionLight.cs b/src/DynamicData/Kernel/ReadOnlyCollectionLight.cs similarity index 100% rename from DynamicData/Kernel/ReadOnlyCollectionLight.cs rename to src/DynamicData/Kernel/ReadOnlyCollectionLight.cs diff --git a/DynamicData/Kernel/ReferenceEqualityComparer.cs b/src/DynamicData/Kernel/ReferenceEqualityComparer.cs similarity index 100% rename from DynamicData/Kernel/ReferenceEqualityComparer.cs rename to src/DynamicData/Kernel/ReferenceEqualityComparer.cs diff --git a/DynamicData/List/Change.cs b/src/DynamicData/List/Change.cs similarity index 100% rename from DynamicData/List/Change.cs rename to src/DynamicData/List/Change.cs diff --git a/DynamicData/List/ChangeAwareList.cs b/src/DynamicData/List/ChangeAwareList.cs similarity index 100% rename from DynamicData/List/ChangeAwareList.cs rename to src/DynamicData/List/ChangeAwareList.cs diff --git a/DynamicData/List/ChangeAwareListWithRefCounts.cs b/src/DynamicData/List/ChangeAwareListWithRefCounts.cs similarity index 100% rename from DynamicData/List/ChangeAwareListWithRefCounts.cs rename to src/DynamicData/List/ChangeAwareListWithRefCounts.cs diff --git a/DynamicData/List/ChangeSet.cs b/src/DynamicData/List/ChangeSet.cs similarity index 100% rename from DynamicData/List/ChangeSet.cs rename to src/DynamicData/List/ChangeSet.cs diff --git a/DynamicData/List/ChangeSetEx.cs b/src/DynamicData/List/ChangeSetEx.cs similarity index 100% rename from DynamicData/List/ChangeSetEx.cs rename to src/DynamicData/List/ChangeSetEx.cs diff --git a/DynamicData/List/ChangeType.cs b/src/DynamicData/List/ChangeType.cs similarity index 100% rename from DynamicData/List/ChangeType.cs rename to src/DynamicData/List/ChangeType.cs diff --git a/DynamicData/List/IChangeSet.cs b/src/DynamicData/List/IChangeSet.cs similarity index 100% rename from DynamicData/List/IChangeSet.cs rename to src/DynamicData/List/IChangeSet.cs diff --git a/DynamicData/List/IChangeSetAdaptor.cs b/src/DynamicData/List/IChangeSetAdaptor.cs similarity index 100% rename from DynamicData/List/IChangeSetAdaptor.cs rename to src/DynamicData/List/IChangeSetAdaptor.cs diff --git a/DynamicData/List/IExtendedList.cs b/src/DynamicData/List/IExtendedList.cs similarity index 100% rename from DynamicData/List/IExtendedList.cs rename to src/DynamicData/List/IExtendedList.cs diff --git a/DynamicData/List/IGroup.cs b/src/DynamicData/List/IGroup.cs similarity index 100% rename from DynamicData/List/IGroup.cs rename to src/DynamicData/List/IGroup.cs diff --git a/DynamicData/List/IGrouping.cs b/src/DynamicData/List/IGrouping.cs similarity index 100% rename from DynamicData/List/IGrouping.cs rename to src/DynamicData/List/IGrouping.cs diff --git a/DynamicData/List/IObservableList.cs b/src/DynamicData/List/IObservableList.cs similarity index 100% rename from DynamicData/List/IObservableList.cs rename to src/DynamicData/List/IObservableList.cs diff --git a/DynamicData/List/IPageChangeSet.cs b/src/DynamicData/List/IPageChangeSet.cs similarity index 100% rename from DynamicData/List/IPageChangeSet.cs rename to src/DynamicData/List/IPageChangeSet.cs diff --git a/DynamicData/List/ISourceList.cs b/src/DynamicData/List/ISourceList.cs similarity index 100% rename from DynamicData/List/ISourceList.cs rename to src/DynamicData/List/ISourceList.cs diff --git a/DynamicData/List/IVirtualChangeSet.cs b/src/DynamicData/List/IVirtualChangeSet.cs similarity index 100% rename from DynamicData/List/IVirtualChangeSet.cs rename to src/DynamicData/List/IVirtualChangeSet.cs diff --git a/DynamicData/List/Internal/AnonymousObservableList.cs b/src/DynamicData/List/Internal/AnonymousObservableList.cs similarity index 100% rename from DynamicData/List/Internal/AnonymousObservableList.cs rename to src/DynamicData/List/Internal/AnonymousObservableList.cs diff --git a/DynamicData/List/Internal/AutoRefresh.cs b/src/DynamicData/List/Internal/AutoRefresh.cs similarity index 100% rename from DynamicData/List/Internal/AutoRefresh.cs rename to src/DynamicData/List/Internal/AutoRefresh.cs diff --git a/DynamicData/List/Internal/BufferIf.cs b/src/DynamicData/List/Internal/BufferIf.cs similarity index 100% rename from DynamicData/List/Internal/BufferIf.cs rename to src/DynamicData/List/Internal/BufferIf.cs diff --git a/DynamicData/List/Internal/Combiner.cs b/src/DynamicData/List/Internal/Combiner.cs similarity index 100% rename from DynamicData/List/Internal/Combiner.cs rename to src/DynamicData/List/Internal/Combiner.cs diff --git a/DynamicData/List/Internal/DeferUntilLoaded.cs b/src/DynamicData/List/Internal/DeferUntilLoaded.cs similarity index 100% rename from DynamicData/List/Internal/DeferUntilLoaded.cs rename to src/DynamicData/List/Internal/DeferUntilLoaded.cs diff --git a/DynamicData/List/Internal/Distinct.cs b/src/DynamicData/List/Internal/Distinct.cs similarity index 100% rename from DynamicData/List/Internal/Distinct.cs rename to src/DynamicData/List/Internal/Distinct.cs diff --git a/DynamicData/List/Internal/DynamicCombiner.cs b/src/DynamicData/List/Internal/DynamicCombiner.cs similarity index 100% rename from DynamicData/List/Internal/DynamicCombiner.cs rename to src/DynamicData/List/Internal/DynamicCombiner.cs diff --git a/DynamicData/List/Internal/EditDiff.cs b/src/DynamicData/List/Internal/EditDiff.cs similarity index 100% rename from DynamicData/List/Internal/EditDiff.cs rename to src/DynamicData/List/Internal/EditDiff.cs diff --git a/DynamicData/List/Internal/ExpirableItem.cs b/src/DynamicData/List/Internal/ExpirableItem.cs similarity index 100% rename from DynamicData/List/Internal/ExpirableItem.cs rename to src/DynamicData/List/Internal/ExpirableItem.cs diff --git a/DynamicData/List/Internal/ExpireAfter.cs b/src/DynamicData/List/Internal/ExpireAfter.cs similarity index 100% rename from DynamicData/List/Internal/ExpireAfter.cs rename to src/DynamicData/List/Internal/ExpireAfter.cs diff --git a/DynamicData/List/Internal/Filter.cs b/src/DynamicData/List/Internal/Filter.cs similarity index 97% rename from DynamicData/List/Internal/Filter.cs rename to src/DynamicData/List/Internal/Filter.cs index 73e0723fb..b4a01f68a 100644 --- a/DynamicData/List/Internal/Filter.cs +++ b/src/DynamicData/List/Internal/Filter.cs @@ -1,290 +1,290 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reactive.Linq; -using DynamicData.Annotations; -using DynamicData.Kernel; - -namespace DynamicData.List.Internal -{ - internal class Filter - { - private readonly ListFilterPolicy _policy; - private readonly IObservable> _source; - private readonly IObservable> _predicates; - private readonly Func _predicate; - - public Filter([NotNull] IObservable> source, - [NotNull] IObservable> predicates, - ListFilterPolicy policy = ListFilterPolicy.CalculateDiff) - { - _policy = policy; - _source = source ?? throw new ArgumentNullException(nameof(source)); - _predicates = predicates ?? throw new ArgumentNullException(nameof(predicates)); - } - - public Filter([NotNull] IObservable> source, - [NotNull] Func predicate, - ListFilterPolicy policy = ListFilterPolicy.CalculateDiff) - { - _policy = policy; - _source = source ?? throw new ArgumentNullException(nameof(source)); - _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); - } - - public IObservable> Run() - { - return Observable.Create>(observer => - { - var locker = new object(); - - Func predicate = t => false; - var all = new List(); - var filtered = new ChangeAwareList(); - var immutableFilter = _predicate != null; - - IObservable> predicateChanged; - - if (immutableFilter) - { - predicateChanged = Observable.Never>(); - predicate = _predicate; - } - else - { - predicateChanged = _predicates - .Synchronize(locker) - .Select(newPredicate => - { - predicate = newPredicate; - return Requery(predicate, all, filtered); - }); - } - - /* - * Apply the transform operator so 'IsMatch' state can be evaluated and captured one time only - * This is to eliminate the need to re-apply the predicate when determining whether an item was previously matched, - * which is essential when we have mutable state - */ - - //Need to get item by index and store it in the transform - var filteredResult = _source - .Synchronize(locker) - .Transform((t, previous) => - { - var wasMatch = previous.ConvertOr(p => p.IsMatch, () => false); - return new ItemWithMatch(t, predicate(t), wasMatch); - },true) - .Select(changes => - { - //keep track of all changes if filtering on an observable - if (!immutableFilter) all.Clone(changes); - return Process(filtered, changes); - }); - - return predicateChanged.Merge(filteredResult) - .NotEmpty() - .Select(changes => changes.Transform(iwm => iwm.Item)) // use convert, not transform - .SubscribeSafe(observer); - }); - } - - private IChangeSet Process(ChangeAwareList filtered, IChangeSet changes) - { - //Maintain all items as well as filtered list. This enables us to a) requery when the predicate changes b) check the previous state when Refresh is called - foreach (var item in changes) - { - switch (item.Reason) - { - case ListChangeReason.Add: - { - var change = item.Item; - if (change.Current.IsMatch) - filtered.Add(change.Current); - break; - } - case ListChangeReason.AddRange: - { - var matches = item.Range.Where(t => t.IsMatch).ToList(); - filtered.AddRange(matches); - break; - } - case ListChangeReason.Replace: - { - var change = item.Item; - var match = change.Current.IsMatch; - var wasMatch = item.Item.Current.WasMatch; - if (match) - { - if (wasMatch) - { - //an update, so get the latest index and pass the index up the chain - var previous = filtered.Select(x => x.Item) - .IndexOfOptional(change.Previous.Value.Item) - .ValueOrThrow(() => new InvalidOperationException($"Cannot find index of {typeof(T).Name} -> {change.Previous.Value}. Expected to be in the list")); - - //replace inline - filtered[previous.Index] = change.Current; - } - else - { - filtered.Add(change.Current); - } - } - else - { - if (wasMatch) - filtered.Remove(change.Previous.Value); - } - break; - } - case ListChangeReason.Refresh: - { - var change = item.Item; - var match = change.Current.IsMatch; - var wasMatch = item.Item.Current.WasMatch; - if (match) - { - if (wasMatch) - { - //an update, so get the latest index and pass the index up the chain - var previous = filtered.Select(x => x.Item) - .IndexOfOptional(change.Current.Item) - .ValueOrThrow(() => new InvalidOperationException($"Cannot find index of {typeof(T).Name} -> {change.Previous.Value}. Expected to be in the list")); - - filtered.RefreshAt(previous.Index); - } - else - { - filtered.Add(change.Current); - } - } - else - { - if (wasMatch) - filtered.Remove(change.Current); - } - break; - } - case ListChangeReason.Remove: - { - filtered.Remove(item.Item.Current); - break; - } - case ListChangeReason.RemoveRange: - { - filtered.RemoveMany(item.Range); - break; - } - case ListChangeReason.Clear: - { - filtered.ClearOrRemoveMany(item); - break; - } - } - - } - return filtered.CaptureChanges(); - } - - private IChangeSet Requery(Func predicate, List all, ChangeAwareList filtered) - { - if (all.Count == 0) return ChangeSet.Empty; - - if (_policy == ListFilterPolicy.ClearAndReplace) - { - var itemsWithMatch = all.Select(iwm => new ItemWithMatch(iwm.Item, predicate(iwm.Item), iwm.IsMatch)).ToList(); - - //mark items as matched? - filtered.Clear(); - filtered.AddRange(itemsWithMatch.Where(iwm => iwm.IsMatch)); - - //reset state for all items - all.Clear(); - all.AddRange(itemsWithMatch); - return filtered.CaptureChanges(); - } - - var toAdd = new List(all.Count); - var toRemove = new List(all.Count); - - for (int i = 0; i < all.Count; i++) - { - var original = all[i]; - - var newItem = new ItemWithMatch(original.Item, predicate(original.Item), original.IsMatch); - all[i] = newItem; - - if (newItem.IsMatch && !newItem.WasMatch) - { - toAdd.Add(newItem); - } - else if (!newItem.IsMatch && newItem.WasMatch) - { - toRemove.Add(newItem); - } - } - - filtered.RemoveMany(toRemove); - filtered.AddRange(toAdd); - - return filtered.CaptureChanges(); - } - - private readonly struct ItemWithMatch : IEquatable - { - public T Item { get; } - public bool IsMatch { get; } - public bool WasMatch { get; } - - public ItemWithMatch(T item, bool isMatch, bool wasMatch = false) - :this() - { - Item = item; - IsMatch = isMatch; - WasMatch = wasMatch; - } - - #region Equality - - public bool Equals(ItemWithMatch other) - { - return EqualityComparer.Default.Equals(Item, other.Item); - } - - public override bool Equals(object obj) - { - if (obj is null) return false; - if (obj.GetType() != GetType()) return false; - return Equals((ItemWithMatch) obj); - } - - public override int GetHashCode() - { - return EqualityComparer.Default.GetHashCode(Item); - } - - /// Returns a value that indicates whether the values of two objects are equal. - /// The first value to compare. - /// The second value to compare. - /// true if the and parameters have the same value; otherwise, false. - public static bool operator ==(ItemWithMatch left, ItemWithMatch right) - { - return Equals(left, right); - } - - /// Returns a value that indicates whether two objects have different values. - /// The first value to compare. - /// The second value to compare. - /// true if and are not equal; otherwise, false. - public static bool operator !=(ItemWithMatch left, ItemWithMatch right) - { - return !Equals(left, right); - } - - #endregion - - public override string ToString() => $"{Item}, (was {IsMatch} is {WasMatch}"; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using DynamicData.Annotations; +using DynamicData.Kernel; + +namespace DynamicData.List.Internal +{ + internal class Filter + { + private readonly ListFilterPolicy _policy; + private readonly IObservable> _source; + private readonly IObservable> _predicates; + private readonly Func _predicate; + + public Filter([NotNull] IObservable> source, + [NotNull] IObservable> predicates, + ListFilterPolicy policy = ListFilterPolicy.CalculateDiff) + { + _policy = policy; + _source = source ?? throw new ArgumentNullException(nameof(source)); + _predicates = predicates ?? throw new ArgumentNullException(nameof(predicates)); + } + + public Filter([NotNull] IObservable> source, + [NotNull] Func predicate, + ListFilterPolicy policy = ListFilterPolicy.CalculateDiff) + { + _policy = policy; + _source = source ?? throw new ArgumentNullException(nameof(source)); + _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); + } + + public IObservable> Run() + { + return Observable.Create>(observer => + { + var locker = new object(); + + Func predicate = t => false; + var all = new List(); + var filtered = new ChangeAwareList(); + var immutableFilter = _predicate != null; + + IObservable> predicateChanged; + + if (immutableFilter) + { + predicateChanged = Observable.Never>(); + predicate = _predicate; + } + else + { + predicateChanged = _predicates + .Synchronize(locker) + .Select(newPredicate => + { + predicate = newPredicate; + return Requery(predicate, all, filtered); + }); + } + + /* + * Apply the transform operator so 'IsMatch' state can be evaluated and captured one time only + * This is to eliminate the need to re-apply the predicate when determining whether an item was previously matched, + * which is essential when we have mutable state + */ + + //Need to get item by index and store it in the transform + var filteredResult = _source + .Synchronize(locker) + .Transform((t, previous) => + { + var wasMatch = previous.ConvertOr(p => p.IsMatch, () => false); + return new ItemWithMatch(t, predicate(t), wasMatch); + },true) + .Select(changes => + { + //keep track of all changes if filtering on an observable + if (!immutableFilter) all.Clone(changes); + return Process(filtered, changes); + }); + + return predicateChanged.Merge(filteredResult) + .NotEmpty() + .Select(changes => changes.Transform(iwm => iwm.Item)) // use convert, not transform + .SubscribeSafe(observer); + }); + } + + private IChangeSet Process(ChangeAwareList filtered, IChangeSet changes) + { + //Maintain all items as well as filtered list. This enables us to a) requery when the predicate changes b) check the previous state when Refresh is called + foreach (var item in changes) + { + switch (item.Reason) + { + case ListChangeReason.Add: + { + var change = item.Item; + if (change.Current.IsMatch) + filtered.Add(change.Current); + break; + } + case ListChangeReason.AddRange: + { + var matches = item.Range.Where(t => t.IsMatch).ToList(); + filtered.AddRange(matches); + break; + } + case ListChangeReason.Replace: + { + var change = item.Item; + var match = change.Current.IsMatch; + var wasMatch = item.Item.Current.WasMatch; + if (match) + { + if (wasMatch) + { + //an update, so get the latest index and pass the index up the chain + var previous = filtered.Select(x => x.Item) + .IndexOfOptional(change.Previous.Value.Item) + .ValueOrThrow(() => new InvalidOperationException($"Cannot find index of {typeof(T).Name} -> {change.Previous.Value}. Expected to be in the list")); + + //replace inline + filtered[previous.Index] = change.Current; + } + else + { + filtered.Add(change.Current); + } + } + else + { + if (wasMatch) + filtered.Remove(change.Previous.Value); + } + break; + } + case ListChangeReason.Refresh: + { + var change = item.Item; + var match = change.Current.IsMatch; + var wasMatch = item.Item.Current.WasMatch; + if (match) + { + if (wasMatch) + { + //an update, so get the latest index and pass the index up the chain + var previous = filtered.Select(x => x.Item) + .IndexOfOptional(change.Current.Item) + .ValueOrThrow(() => new InvalidOperationException($"Cannot find index of {typeof(T).Name} -> {change.Previous.Value}. Expected to be in the list")); + + filtered.RefreshAt(previous.Index); + } + else + { + filtered.Add(change.Current); + } + } + else + { + if (wasMatch) + filtered.Remove(change.Current); + } + break; + } + case ListChangeReason.Remove: + { + filtered.Remove(item.Item.Current); + break; + } + case ListChangeReason.RemoveRange: + { + filtered.RemoveMany(item.Range); + break; + } + case ListChangeReason.Clear: + { + filtered.ClearOrRemoveMany(item); + break; + } + } + + } + return filtered.CaptureChanges(); + } + + private IChangeSet Requery(Func predicate, List all, ChangeAwareList filtered) + { + if (all.Count == 0) return ChangeSet.Empty; + + if (_policy == ListFilterPolicy.ClearAndReplace) + { + var itemsWithMatch = all.Select(iwm => new ItemWithMatch(iwm.Item, predicate(iwm.Item), iwm.IsMatch)).ToList(); + + //mark items as matched? + filtered.Clear(); + filtered.AddRange(itemsWithMatch.Where(iwm => iwm.IsMatch)); + + //reset state for all items + all.Clear(); + all.AddRange(itemsWithMatch); + return filtered.CaptureChanges(); + } + + var toAdd = new List(all.Count); + var toRemove = new List(all.Count); + + for (int i = 0; i < all.Count; i++) + { + var original = all[i]; + + var newItem = new ItemWithMatch(original.Item, predicate(original.Item), original.IsMatch); + all[i] = newItem; + + if (newItem.IsMatch && !newItem.WasMatch) + { + toAdd.Add(newItem); + } + else if (!newItem.IsMatch && newItem.WasMatch) + { + toRemove.Add(newItem); + } + } + + filtered.RemoveMany(toRemove); + filtered.AddRange(toAdd); + + return filtered.CaptureChanges(); + } + + private readonly struct ItemWithMatch : IEquatable + { + public T Item { get; } + public bool IsMatch { get; } + public bool WasMatch { get; } + + public ItemWithMatch(T item, bool isMatch, bool wasMatch = false) + :this() + { + Item = item; + IsMatch = isMatch; + WasMatch = wasMatch; + } + + #region Equality + + public bool Equals(ItemWithMatch other) + { + return EqualityComparer.Default.Equals(Item, other.Item); + } + + public override bool Equals(object obj) + { + if (obj is null) return false; + if (obj.GetType() != GetType()) return false; + return Equals((ItemWithMatch) obj); + } + + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(Item); + } + + /// Returns a value that indicates whether the values of two objects are equal. + /// The first value to compare. + /// The second value to compare. + /// true if the and parameters have the same value; otherwise, false. + public static bool operator ==(ItemWithMatch left, ItemWithMatch right) + { + return Equals(left, right); + } + + /// Returns a value that indicates whether two objects have different values. + /// The first value to compare. + /// The second value to compare. + /// true if and are not equal; otherwise, false. + public static bool operator !=(ItemWithMatch left, ItemWithMatch right) + { + return !Equals(left, right); + } + + #endregion + + public override string ToString() => $"{Item}, (was {IsMatch} is {WasMatch}"; + } + } +} diff --git a/DynamicData/List/Internal/FilterOnObservable.cs b/src/DynamicData/List/Internal/FilterOnObservable.cs similarity index 100% rename from DynamicData/List/Internal/FilterOnObservable.cs rename to src/DynamicData/List/Internal/FilterOnObservable.cs diff --git a/DynamicData/List/Internal/FilterOnProperty.cs b/src/DynamicData/List/Internal/FilterOnProperty.cs similarity index 100% rename from DynamicData/List/Internal/FilterOnProperty.cs rename to src/DynamicData/List/Internal/FilterOnProperty.cs diff --git a/DynamicData/List/Internal/FilterStatic.cs b/src/DynamicData/List/Internal/FilterStatic.cs similarity index 100% rename from DynamicData/List/Internal/FilterStatic.cs rename to src/DynamicData/List/Internal/FilterStatic.cs diff --git a/DynamicData/List/Internal/Group.cs b/src/DynamicData/List/Internal/Group.cs similarity index 100% rename from DynamicData/List/Internal/Group.cs rename to src/DynamicData/List/Internal/Group.cs diff --git a/DynamicData/List/Internal/GroupOn.cs b/src/DynamicData/List/Internal/GroupOn.cs similarity index 100% rename from DynamicData/List/Internal/GroupOn.cs rename to src/DynamicData/List/Internal/GroupOn.cs diff --git a/DynamicData/List/Internal/GroupOnImmutable.cs b/src/DynamicData/List/Internal/GroupOnImmutable.cs similarity index 100% rename from DynamicData/List/Internal/GroupOnImmutable.cs rename to src/DynamicData/List/Internal/GroupOnImmutable.cs diff --git a/DynamicData/List/Internal/GroupOnProperty.cs b/src/DynamicData/List/Internal/GroupOnProperty.cs similarity index 100% rename from DynamicData/List/Internal/GroupOnProperty.cs rename to src/DynamicData/List/Internal/GroupOnProperty.cs diff --git a/DynamicData/List/Internal/GroupOnPropertyWithImmutableState.cs b/src/DynamicData/List/Internal/GroupOnPropertyWithImmutableState.cs similarity index 100% rename from DynamicData/List/Internal/GroupOnPropertyWithImmutableState.cs rename to src/DynamicData/List/Internal/GroupOnPropertyWithImmutableState.cs diff --git a/DynamicData/List/Internal/ImmutableGroup.cs b/src/DynamicData/List/Internal/ImmutableGroup.cs similarity index 100% rename from DynamicData/List/Internal/ImmutableGroup.cs rename to src/DynamicData/List/Internal/ImmutableGroup.cs diff --git a/DynamicData/List/Internal/MergeMany.cs b/src/DynamicData/List/Internal/MergeMany.cs similarity index 100% rename from DynamicData/List/Internal/MergeMany.cs rename to src/DynamicData/List/Internal/MergeMany.cs diff --git a/DynamicData/List/Internal/OnBeingAdded.cs b/src/DynamicData/List/Internal/OnBeingAdded.cs similarity index 100% rename from DynamicData/List/Internal/OnBeingAdded.cs rename to src/DynamicData/List/Internal/OnBeingAdded.cs diff --git a/DynamicData/List/Internal/OnBeingRemoved.cs b/src/DynamicData/List/Internal/OnBeingRemoved.cs similarity index 100% rename from DynamicData/List/Internal/OnBeingRemoved.cs rename to src/DynamicData/List/Internal/OnBeingRemoved.cs diff --git a/DynamicData/List/Internal/Pager.cs b/src/DynamicData/List/Internal/Pager.cs similarity index 100% rename from DynamicData/List/Internal/Pager.cs rename to src/DynamicData/List/Internal/Pager.cs diff --git a/DynamicData/List/Internal/QueryWhenChanged.cs b/src/DynamicData/List/Internal/QueryWhenChanged.cs similarity index 100% rename from DynamicData/List/Internal/QueryWhenChanged.cs rename to src/DynamicData/List/Internal/QueryWhenChanged.cs diff --git a/DynamicData/List/Internal/ReaderWriter.cs b/src/DynamicData/List/Internal/ReaderWriter.cs similarity index 100% rename from DynamicData/List/Internal/ReaderWriter.cs rename to src/DynamicData/List/Internal/ReaderWriter.cs diff --git a/DynamicData/List/Internal/RefCount.cs b/src/DynamicData/List/Internal/RefCount.cs similarity index 100% rename from DynamicData/List/Internal/RefCount.cs rename to src/DynamicData/List/Internal/RefCount.cs diff --git a/DynamicData/List/Internal/ReferenceCountTracker.cs b/src/DynamicData/List/Internal/ReferenceCountTracker.cs similarity index 100% rename from DynamicData/List/Internal/ReferenceCountTracker.cs rename to src/DynamicData/List/Internal/ReferenceCountTracker.cs diff --git a/DynamicData/List/Internal/SizeLimiter.cs b/src/DynamicData/List/Internal/SizeLimiter.cs similarity index 100% rename from DynamicData/List/Internal/SizeLimiter.cs rename to src/DynamicData/List/Internal/SizeLimiter.cs diff --git a/DynamicData/List/Internal/Sort.cs b/src/DynamicData/List/Internal/Sort.cs similarity index 100% rename from DynamicData/List/Internal/Sort.cs rename to src/DynamicData/List/Internal/Sort.cs diff --git a/DynamicData/List/Internal/SubscribeMany.cs b/src/DynamicData/List/Internal/SubscribeMany.cs similarity index 100% rename from DynamicData/List/Internal/SubscribeMany.cs rename to src/DynamicData/List/Internal/SubscribeMany.cs diff --git a/DynamicData/List/Internal/Switch.cs b/src/DynamicData/List/Internal/Switch.cs similarity index 100% rename from DynamicData/List/Internal/Switch.cs rename to src/DynamicData/List/Internal/Switch.cs diff --git a/DynamicData/List/Internal/ToObservableChangeSet.cs b/src/DynamicData/List/Internal/ToObservableChangeSet.cs similarity index 100% rename from DynamicData/List/Internal/ToObservableChangeSet.cs rename to src/DynamicData/List/Internal/ToObservableChangeSet.cs diff --git a/DynamicData/List/Internal/TransformAsync.cs b/src/DynamicData/List/Internal/TransformAsync.cs similarity index 100% rename from DynamicData/List/Internal/TransformAsync.cs rename to src/DynamicData/List/Internal/TransformAsync.cs diff --git a/DynamicData/List/Internal/TransformMany.cs b/src/DynamicData/List/Internal/TransformMany.cs similarity index 100% rename from DynamicData/List/Internal/TransformMany.cs rename to src/DynamicData/List/Internal/TransformMany.cs diff --git a/DynamicData/List/Internal/Transformer.cs b/src/DynamicData/List/Internal/Transformer.cs similarity index 100% rename from DynamicData/List/Internal/Transformer.cs rename to src/DynamicData/List/Internal/Transformer.cs diff --git a/DynamicData/List/Internal/UnifiedChange.cs b/src/DynamicData/List/Internal/UnifiedChange.cs similarity index 100% rename from DynamicData/List/Internal/UnifiedChange.cs rename to src/DynamicData/List/Internal/UnifiedChange.cs diff --git a/DynamicData/List/Internal/Virtualiser.cs b/src/DynamicData/List/Internal/Virtualiser.cs similarity index 100% rename from DynamicData/List/Internal/Virtualiser.cs rename to src/DynamicData/List/Internal/Virtualiser.cs diff --git a/DynamicData/List/ItemChange.cs b/src/DynamicData/List/ItemChange.cs similarity index 97% rename from DynamicData/List/ItemChange.cs rename to src/DynamicData/List/ItemChange.cs index abff6ec93..54d03460b 100644 --- a/DynamicData/List/ItemChange.cs +++ b/src/DynamicData/List/ItemChange.cs @@ -1,159 +1,159 @@ -using System; -using System.Collections.Generic; -using DynamicData.Kernel; - -// ReSharper disable once CheckNamespace -namespace DynamicData -{ - /// - /// Container to describe a single change to a cache - /// - /// - public readonly struct ItemChange : IEquatable> - { - /// - /// An empty change - /// - public static readonly ItemChange Empty = new ItemChange(); - - /// - /// The reason for the change - /// - public ListChangeReason Reason { get; } - - /// - /// The item which has changed - /// - public T Current { get; } - - /// - /// The current index - /// - public int CurrentIndex { get; } - - /// - /// The previous change. - /// - /// This is only when Reason==ChangeReason.Replace. - /// - public Optional Previous { get; } - - /// - /// The previous index. - /// - /// This is only when Reason==ChangeReason.Replace or ChangeReason.Move. - /// - public int PreviousIndex { get; } - - #region Construction - - /// - /// Initializes a new instance of the struct. - /// - /// The reason. - /// The current. - /// The previous. - /// Value of the current. - /// Value of the previous. - /// For ChangeReason.Add, a previous value cannot be specified - /// or - /// For ChangeReason.Change, must supply previous value - /// For ChangeReason.Add, a previous value cannot be specified - /// or - /// For ChangeReason.Change, must supply previous value - public ItemChange(ListChangeReason reason, T current, Optional previous, int currentIndex = -1, int previousIndex = -1) - : this() - { - Reason = reason; - Current = current; - Previous = previous; - CurrentIndex = currentIndex; - PreviousIndex = previousIndex; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The reason. - /// The current. - /// Index of the current. - public ItemChange(ListChangeReason reason, T current, int currentIndex) - : this() - { - Reason = reason; - Current = current; - CurrentIndex = currentIndex; - PreviousIndex = -1; - Previous = Optional.None; - } - - #endregion - - #region Equality - - /// - /// Determines whether the specified object, is equal to this instance. - /// - /// The other. - /// - public bool Equals(ItemChange other) - { - return EqualityComparer.Default.Equals(Current, other.Current) && CurrentIndex == other.CurrentIndex && Previous.Equals(other.Previous) && PreviousIndex == other.PreviousIndex; - } - - /// - /// Determines whether the specified , is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - return obj is ItemChange && Equals((ItemChange)obj); - } - - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Current); - hashCode = (hashCode * 397) ^ CurrentIndex; - hashCode = (hashCode * 397) ^ Previous.GetHashCode(); - hashCode = (hashCode * 397) ^ PreviousIndex; - return hashCode; - } - } - - /// - /// Determines whether the specified objects are equal - /// - public static bool operator ==(ItemChange left, ItemChange right) => left.Equals(right); - - - /// - /// Determines whether the specified objects are not equal - /// - public static bool operator !=(ItemChange left, ItemChange right) => !left.Equals(right); - - #endregion - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return $"Current: {Current}, Previous: {Previous}"; - } - } -} +using System; +using System.Collections.Generic; +using DynamicData.Kernel; + +// ReSharper disable once CheckNamespace +namespace DynamicData +{ + /// + /// Container to describe a single change to a cache + /// + /// + public readonly struct ItemChange : IEquatable> + { + /// + /// An empty change + /// + public static readonly ItemChange Empty = new ItemChange(); + + /// + /// The reason for the change + /// + public ListChangeReason Reason { get; } + + /// + /// The item which has changed + /// + public T Current { get; } + + /// + /// The current index + /// + public int CurrentIndex { get; } + + /// + /// The previous change. + /// + /// This is only when Reason==ChangeReason.Replace. + /// + public Optional Previous { get; } + + /// + /// The previous index. + /// + /// This is only when Reason==ChangeReason.Replace or ChangeReason.Move. + /// + public int PreviousIndex { get; } + + #region Construction + + /// + /// Initializes a new instance of the struct. + /// + /// The reason. + /// The current. + /// The previous. + /// Value of the current. + /// Value of the previous. + /// For ChangeReason.Add, a previous value cannot be specified + /// or + /// For ChangeReason.Change, must supply previous value + /// For ChangeReason.Add, a previous value cannot be specified + /// or + /// For ChangeReason.Change, must supply previous value + public ItemChange(ListChangeReason reason, T current, Optional previous, int currentIndex = -1, int previousIndex = -1) + : this() + { + Reason = reason; + Current = current; + Previous = previous; + CurrentIndex = currentIndex; + PreviousIndex = previousIndex; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The reason. + /// The current. + /// Index of the current. + public ItemChange(ListChangeReason reason, T current, int currentIndex) + : this() + { + Reason = reason; + Current = current; + CurrentIndex = currentIndex; + PreviousIndex = -1; + Previous = Optional.None; + } + + #endregion + + #region Equality + + /// + /// Determines whether the specified object, is equal to this instance. + /// + /// The other. + /// + public bool Equals(ItemChange other) + { + return EqualityComparer.Default.Equals(Current, other.Current) && CurrentIndex == other.CurrentIndex && Previous.Equals(other.Previous) && PreviousIndex == other.PreviousIndex; + } + + /// + /// Determines whether the specified , is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is ItemChange && Equals((ItemChange)obj); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + var hashCode = EqualityComparer.Default.GetHashCode(Current); + hashCode = (hashCode * 397) ^ CurrentIndex; + hashCode = (hashCode * 397) ^ Previous.GetHashCode(); + hashCode = (hashCode * 397) ^ PreviousIndex; + return hashCode; + } + } + + /// + /// Determines whether the specified objects are equal + /// + public static bool operator ==(ItemChange left, ItemChange right) => left.Equals(right); + + + /// + /// Determines whether the specified objects are not equal + /// + public static bool operator !=(ItemChange left, ItemChange right) => !left.Equals(right); + + #endregion + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return $"Current: {Current}, Previous: {Previous}"; + } + } +} diff --git a/DynamicData/List/Linq/AddKeyEnumerator.cs b/src/DynamicData/List/Linq/AddKeyEnumerator.cs similarity index 100% rename from DynamicData/List/Linq/AddKeyEnumerator.cs rename to src/DynamicData/List/Linq/AddKeyEnumerator.cs diff --git a/DynamicData/List/Linq/ItemChangeEnumerator.cs b/src/DynamicData/List/Linq/ItemChangeEnumerator.cs similarity index 100% rename from DynamicData/List/Linq/ItemChangeEnumerator.cs rename to src/DynamicData/List/Linq/ItemChangeEnumerator.cs diff --git a/DynamicData/List/Linq/ReverseEnumerator.cs b/src/DynamicData/List/Linq/ReverseEnumerator.cs similarity index 100% rename from DynamicData/List/Linq/ReverseEnumerator.cs rename to src/DynamicData/List/Linq/ReverseEnumerator.cs diff --git a/DynamicData/List/Linq/UnifiedChangeEnumerator.cs b/src/DynamicData/List/Linq/UnifiedChangeEnumerator.cs similarity index 100% rename from DynamicData/List/Linq/UnifiedChangeEnumerator.cs rename to src/DynamicData/List/Linq/UnifiedChangeEnumerator.cs diff --git a/DynamicData/List/Linq/WithoutIndexEnumerator.cs b/src/DynamicData/List/Linq/WithoutIndexEnumerator.cs similarity index 100% rename from DynamicData/List/Linq/WithoutIndexEnumerator.cs rename to src/DynamicData/List/Linq/WithoutIndexEnumerator.cs diff --git a/DynamicData/List/ListChangeReason.cs b/src/DynamicData/List/ListChangeReason.cs similarity index 100% rename from DynamicData/List/ListChangeReason.cs rename to src/DynamicData/List/ListChangeReason.cs diff --git a/DynamicData/List/ListEx.cs b/src/DynamicData/List/ListEx.cs similarity index 100% rename from DynamicData/List/ListEx.cs rename to src/DynamicData/List/ListEx.cs diff --git a/DynamicData/List/ListFilterPolicy.cs b/src/DynamicData/List/ListFilterPolicy.cs similarity index 100% rename from DynamicData/List/ListFilterPolicy.cs rename to src/DynamicData/List/ListFilterPolicy.cs diff --git a/DynamicData/List/ObservableListEx.cs b/src/DynamicData/List/ObservableListEx.cs similarity index 98% rename from DynamicData/List/ObservableListEx.cs rename to src/DynamicData/List/ObservableListEx.cs index 4958fe431..afd0be6aa 100644 --- a/DynamicData/List/ObservableListEx.cs +++ b/src/DynamicData/List/ObservableListEx.cs @@ -1,2042 +1,2042 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Linq.Expressions; -using System.Reactive; -using System.Reactive.Concurrency; -using System.Reactive.Disposables; -using System.Reactive.Linq; -using System.Threading.Tasks; -using DynamicData.Annotations; -using DynamicData.Binding; -using DynamicData.Cache.Internal; -using DynamicData.Kernel; -using DynamicData.List.Internal; -using DynamicData.List.Linq; - -// ReSharper disable once CheckNamespace - -namespace DynamicData -{ - /// - /// Extensions for ObservableList - /// - public static class ObservableListEx - { - #region Populate change set from standard rx observable - - /// - /// Converts the observable to an observable changeset. - /// - /// The type of the object. - /// The source. - /// The scheduler (only used for time expiry). - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IObservable source, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - - return ToObservableChangeSet(source, null, -1, scheduler); - } - - /// - /// Converts the observable to an observable changeset, allowing time expiry to be specified - /// - /// The type of the object. - /// The source. - /// Specify on a per object level the maximum time before an object expires from a cache - /// The scheduler (only used for time expiry). - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IObservable source, - Func expireAfter, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (expireAfter == null) throw new ArgumentNullException(nameof(expireAfter)); - - return ToObservableChangeSet(source, expireAfter, -1, scheduler); - } - - /// - /// Converts the observable to an observable changeset, with a specified limit of how large the list can be. - /// - /// The type of the object. - /// The source. - /// Remove the oldest items when the size has reached this limit - /// The scheduler (only used for time expiry). - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IObservable source, - int limitSizeTo, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return ToObservableChangeSet(source, null, limitSizeTo, scheduler); - } - - /// - /// Converts the observable to an observable changeset, allowing size and time limit to be specified - /// - /// The type of the object. - /// The source. - /// Specify on a per object level the maximum time before an object expires from a cache - /// Remove the oldest items when the size has reached this limit - /// The scheduler (only used for time expiry). - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IObservable source, - Func expireAfter, - int limitSizeTo, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return new ToObservableChangeSet(source, expireAfter, limitSizeTo, scheduler).Run(); - } - - /// - /// Converts the observable to an observable changeset. - /// - /// The type of the object. - /// The source. - /// The scheduler (only used for time expiry). - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IObservable> source, - IScheduler scheduler = null) - { - return ToObservableChangeSet(source, null, -1, scheduler); - } - - /// - /// Converts the observable to an observable changeset, allowing size and time limit to be specified - /// - /// The type of the object. - /// The source. - /// Remove the oldest items when the size has reached this limit - /// The scheduler (only used for time expiry). - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IObservable> source, - int limitSizeTo, - IScheduler scheduler = null) - { - return ToObservableChangeSet(source, null, limitSizeTo, scheduler); - } - - /// - /// Converts the observable to an observable changeset, allowing size to be specified - /// - /// The type of the object. - /// The source. - /// Specify on a per object level the maximum time before an object expires from a cache - /// The scheduler (only used for time expiry). - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IObservable> source, - Func expireAfter, - IScheduler scheduler = null) - { - return ToObservableChangeSet(source, expireAfter, 0, scheduler); - } - - /// - /// Converts the observable to an observable changeset, allowing size and time limit to be specified - /// - /// The type of the object. - /// The source. - /// Specify on a per object level the maximum time before an object expires from a cache - /// Remove the oldest items when the size has reached this limit - /// The scheduler (only used for time expiry). - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IObservable> source, - Func expireAfter, - int limitSizeTo, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return new ToObservableChangeSet(source, expireAfter, limitSizeTo, scheduler).Run(); - } - - #endregion - - #region Auto Refresh - - /// - /// Automatically refresh downstream operators when any property changes. - /// - /// The source observable - /// Batch up changes by specifying the buffer. This greatly increases performance when many elements have sucessive property changes - /// When observing on multiple property changes, apply a throttle to prevent excessive refesh invocations - /// The scheduler - /// An observable change set with additional refresh changes - public static IObservable> AutoRefresh(this IObservable> source, - TimeSpan? changeSetBuffer = null, - TimeSpan? propertyChangeThrottle = null, - IScheduler scheduler = null) - where TObject : INotifyPropertyChanged - { - if (source == null) throw new ArgumentNullException(nameof(source)); - - return source.AutoRefreshOnObservable(t => - { - if (propertyChangeThrottle == null) - return t.WhenAnyPropertyChanged(); - - return t.WhenAnyPropertyChanged() - .Throttle(propertyChangeThrottle.Value, scheduler ?? Scheduler.Default); - - }, changeSetBuffer, scheduler); - } - - /// - /// Automatically refresh downstream operators when properties change. - /// - /// The source observable - /// Specify a property to observe changes. When it changes a Refresh is invoked - /// Batch up changes by specifying the buffer. This greatly increases performance when many elements have sucessive property changes - /// When observing on multiple property changes, apply a throttle to prevent excessive refesh invocations - /// The scheduler - /// An observable change set with additional refresh changes - public static IObservable> AutoRefresh(this IObservable> source, - Expression> propertyAccessor, - TimeSpan? changeSetBuffer = null, - TimeSpan? propertyChangeThrottle = null, - IScheduler scheduler = null) - where TObject : INotifyPropertyChanged - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); - - return source.AutoRefreshOnObservable(t => - { - if (propertyChangeThrottle == null) - return t.WhenPropertyChanged(propertyAccessor, false); - - return t.WhenPropertyChanged(propertyAccessor,false) - .Throttle(propertyChangeThrottle.Value, scheduler ?? Scheduler.Default); - - }, changeSetBuffer, scheduler); - } - - /// - /// Automatically refresh downstream operator. The refresh is triggered when the observable receives a notification - /// - /// The source observable change set - /// An observable which acts on items within the collection and produces a value when the item should be refreshed - /// Batch up changes by specifying the buffer. This greatly increases performance when many elements require a refresh - /// The scheduler - /// An observable change set with additional refresh changes - public static IObservable> AutoRefreshOnObservable(this IObservable> source, - Func> reevaluator, - TimeSpan? changeSetBuffer = null, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (reevaluator == null) throw new ArgumentNullException(nameof(reevaluator)); - return new AutoRefresh(source, reevaluator, changeSetBuffer, scheduler).Run(); - } - - - /// - /// Supress refresh notifications - /// - /// The source observable change set - /// - public static IObservable> SupressRefresh(this IObservable> source) - { - return source.WhereReasonsAreNot(ListChangeReason.Refresh); - } - - - #endregion - - #region Conversion - - /// - /// Removes the index from all changes. - /// - /// NB: This operator has been introduced as a temporary fix for creating an Or operator using merge many. - /// - /// The type of the object. - /// The source. - /// - /// - public static IObservable> RemoveIndex([NotNull] this IObservable> source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Select(changes => new ChangeSet(changes.YieldWithoutIndex())); - } - - /// - /// Adds a key to the change set result which enables all observable cache features of dynamic data - /// - /// - /// All indexed changes are dropped i.e. sorting is not supported by this function - /// - /// The type of object. - /// The type of key. - /// The source. - /// The key selector. - /// - /// - /// - public static IObservable> AddKey( - [NotNull] this IObservable> source, [NotNull] Func keySelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - return source.Select(changes => new ChangeSet(new AddKeyEnumerator(changes, keySelector))); - } - - /// - /// Convert the object using the sepcified conversion function. - /// - /// This is a lighter equivalent of Transform and is designed to be used with non-disposable objects - /// - /// The type of the object. - /// The type of the destination. - /// The source. - /// The conversion factory. - /// - /// - /// - [Obsolete("Prefer Cast as it is does the same thing but is semantically correct")] - public static IObservable> Convert( - [NotNull] this IObservable> source, - [NotNull] Func conversionFactory) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (conversionFactory == null) throw new ArgumentNullException(nameof(conversionFactory)); - return source.Select(changes => changes.Transform(conversionFactory)); - } - - - - /// - /// Cast the underlying type of an object. Use before a Cast function - /// - /// - /// The source. - /// - public static IObservable> CastToObject(this IObservable> source) - { - return source.Select(changes => - { - var items = changes.Transform(t => (object)t); - return new ChangeSet(items); - }); - } - - /// - /// Cast the changes to another form - /// - /// The type of the destination. - /// The source. - /// - /// - /// - public static IObservable> Cast([NotNull] this IObservable> source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Select(changes => changes.Transform(t=>(TDestination)t)); - } - - /// - /// Cast the changes to another form - /// - /// Alas, I had to add the converter due to type inference issues. The converter can be avoided by CastToObject() first - /// - /// The type of the object. - /// The type of the destination. - /// The source. - /// The conversion factory. - /// - /// - /// - public static IObservable> Cast([NotNull] this IObservable> source, - [NotNull] Func conversionFactory) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (conversionFactory == null) throw new ArgumentNullException(nameof(conversionFactory)); - return source.Select(changes => changes.Transform(conversionFactory)); - } - - #endregion - - #region Binding - - /// - /// Binds a clone of the observable changeset to the target observable collection - /// - /// - /// The source. - /// The target collection. - /// The reset threshold. - /// - /// - /// source - /// or - /// targetCollection - /// - public static IObservable> Bind([NotNull] this IObservable> source, - [NotNull] IObservableCollection targetCollection, int resetThreshold = 25) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (targetCollection == null) throw new ArgumentNullException(nameof(targetCollection)); - - var adaptor = new ObservableCollectionAdaptor(targetCollection, resetThreshold); - return source.Adapt(adaptor); - } - - /// - /// Creates a binding to a readonly observable collection which is specified as an 'out' parameter - /// - /// - /// The source. - /// The resulting read only observable collection. - /// The reset threshold. - /// A continuation of the source stream - /// - /// - public static IObservable> Bind([NotNull] this IObservable> source, - out ReadOnlyObservableCollection readOnlyObservableCollection, int resetThreshold = 25) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - - var target = new ObservableCollectionExtended(); - var result = new ReadOnlyObservableCollection(target); - var adaptor = new ObservableCollectionAdaptor(target, resetThreshold); - readOnlyObservableCollection = result; - return source.Adapt(adaptor); - } - -#if SUPPORTS_BINDINGLIST - - /// - /// Binds a clone of the observable changeset to the target observable collection - /// - /// - /// The source. - /// The target binding list - /// The reset threshold. - /// - /// source - /// or - /// targetCollection - /// - public static IObservable> Bind([NotNull] this IObservable> source, - [NotNull] BindingList bindingList, int resetThreshold = 25) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (bindingList == null) throw new ArgumentNullException(nameof(bindingList)); - - return source.Adapt(new BindingListAdaptor(bindingList, resetThreshold)); - } - -#endif - - - /// - /// Injects a side effect into a changeset observable - /// - /// - /// The source. - /// The adaptor. - /// - /// - /// source - /// or - /// adaptor - /// - public static IObservable> Adapt([NotNull] this IObservable> source, - [NotNull] IChangeSetAdaptor adaptor) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (adaptor == null) throw new ArgumentNullException(nameof(adaptor)); - - - return Observable.Create>(observer => - { - var locker = new object(); - return source - .Synchronize(locker) - .Select(changes => - { - adaptor.Adapt(changes); - return changes; - }).SubscribeSafe(observer); - }); - } - - #endregion - - #region Populate into an observable list - - /// - /// list. - /// - /// The type of the object. - /// The source. - /// The destination. - /// - /// - /// source - /// or - /// destination - /// - /// source - /// or - /// destination - public static IDisposable PopulateInto([NotNull] this IObservable> source, - [NotNull] ISourceList destination) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); - - return source.Subscribe(changes => destination.Edit(updater => updater.Clone(changes))); - } - - /// - /// Converts the source list to an read only observable list - /// - /// - /// The source. - /// - /// source - public static IObservableList AsObservableList([NotNull] this ISourceList source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return new AnonymousObservableList(source); - } - - /// - /// Converts the source observable to an read only observable list - /// - /// - /// The source. - /// - /// source - public static IObservableList AsObservableList([NotNull] this IObservable> source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return new AnonymousObservableList(source); - } - - /// - /// List equivalent to Publish().RefCount(). The source is cached so long as there is at least 1 subscriber. - /// - /// - /// The source. - /// - /// - public static IObservable> RefCount([NotNull] this IObservable> source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return new RefCount(source).Run(); - } - - #endregion - - #region Core List Operators - - /// - /// Filters the source using the specified valueSelector - /// - /// - /// The source. - /// The valueSelector. - /// - /// source - public static IObservable> Filter(this IObservable> source, Func predicate) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); - return new Filter(source, predicate).Run(); - } - - /// - /// Filters source using the specified filter observable predicate. - /// - /// - /// The source. - /// - /// Should the filter clear and replace, or calculate a diff-set - /// - /// source - /// or - /// filterController - public static IObservable> Filter([NotNull] this IObservable> source, [NotNull] IObservable> predicate, ListFilterPolicy filterPolicy = ListFilterPolicy.CalculateDiff) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); - - return new Filter(source, predicate, filterPolicy).Run(); - } - - - /// - /// Filters source on the specified property using the specified predicate. - /// - /// The filter will automatically reapply when a property changes - /// - /// The type of the object. - /// The type of the property. - /// The source. - /// The property selector. When the property changes the filter specified will be re-evaluated - /// A predicate based on the object which contains the changed property - /// The property changed throttle. - /// The scheduler used when throttling - /// - /// - /// - [Obsolete("Use AutoRefresh(), followed by Filter() instead")] - public static IObservable> FilterOnProperty(this IObservable> source, - Expression> propertySelector, - Func predicate, - TimeSpan? propertyChangedThrottle = null, - IScheduler scheduler = null) where TObject : INotifyPropertyChanged - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertySelector == null) throw new ArgumentNullException(nameof(propertySelector)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); - return new FilterOnProperty(source, propertySelector, predicate, propertyChangedThrottle, scheduler).Run(); - } - - /// - /// Filters source on the specified observable property using the specified predicate. - /// - /// The filter will automatically reapply when a property changes - /// - /// The type of the object. - /// The source. - /// The filter property selector. When the observable changes the filter will be re-evaluated - /// The property changed throttle. - /// The scheduler used when throttling - /// - /// - /// - public static IObservable> FilterOnObservable(this IObservable> source, - Func> objectFilterObservable, - TimeSpan? propertyChangedThrottle = null, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return new FilterOnObservable(source, objectFilterObservable, propertyChangedThrottle, scheduler).Run(); - } - - /// - /// Reverse sort of the changset - /// - /// - /// The source. - /// - /// - /// source - /// or - /// comparer - /// - public static IObservable> Reverse(this IObservable> source) - { - var reverser = new Reverser(); - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Select(changes => new ChangeSet(reverser.Reverse(changes))); - } - - /// - /// Projects each update item to a new form using the specified transform function - /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// Should a new transform be applied when a refresh event is received - /// - /// - /// source - /// or - /// valueSelector - /// - public static IObservable> Transform(this IObservable> source, - Func transformFactory, - bool transformOnRefresh = false) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - return source.Transform((t, previous, idx) => transformFactory(t), transformOnRefresh); - } - - /// - /// Projects each update item to a new form using the specified transform function - /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform function - /// Should a new transform be applied when a refresh event is received - /// A an observable changeset of the transformed object - /// - /// source - /// or - /// valueSelector - /// - public static IObservable> Transform(this IObservable> source, - Func transformFactory, - bool transformOnRefresh = false) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - - return source.Transform((t, previous, idx) => transformFactory(t,idx),transformOnRefresh); - } - - /// - /// Projects each update item to a new form using the specified transform function. - /// - /// *** Annoyingly when using this overload you will have to explicitly specify the generic type arguments as type inference fails - /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform function - /// Should a new transform be applied when a refresh event is received - /// A an observable changeset of the transformed object - /// - /// source - /// or - /// valueSelector - /// - public static IObservable> Transform(this IObservable> source, - Func, TDestination> transformFactory, - bool transformOnRefresh = false) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - - return source.Transform((t, previous, idx) => transformFactory(t, previous), transformOnRefresh); - } - - /// - /// Projects each update item to a new form using the specified transform function - /// - /// *** Annoyingly when using this overload you will have to explicy specify the generic type arguments as type inference fails - /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// Should a new transform be applied when a refresh event is received - /// A an observable changeset of the transformed object - /// - /// source - /// or - /// valueSelector - /// - public static IObservable> Transform(this IObservable> source, - Func, int, TDestination> transformFactory, bool transformOnRefresh = false) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - - return new Transformer(source, transformFactory, transformOnRefresh).Run(); - } - - - /// - /// Projects each update item to a new form using the specified transform function - /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// A an observable changeset of the transformed object - /// - /// source - /// or - /// valueSelector - /// - public static IObservable> TransformAsync( - this IObservable> source, Func> transformFactory) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - - return new TransformAsync(source, transformFactory).Run(); - } - - /// - /// Equivalent to a select many transform. To work, the key must individually identify each child. - /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The manyselector. - /// Used when an item has been replaced to determine whether child items are the same as previous children - /// - /// - /// source - /// or - /// manyselector - /// - public static IObservable> TransformMany( [NotNull] this IObservable> source, - [NotNull] Func> manyselector, - IEqualityComparer equalityComparer = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (manyselector == null) throw new ArgumentNullException(nameof(manyselector)); - return new TransformMany(source, manyselector, equalityComparer).Run(); - } - - /// - /// Flatten the nested observable collection, and observe subsequentl observable collection changes - /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The manyselector. - /// Used when an item has been replaced to determine whether child items are the same as previous children - public static IObservable> TransformMany( this IObservable> source, - Func> manyselector, - IEqualityComparer equalityComparer = null) - { - return new TransformMany(source,manyselector, equalityComparer).Run(); - } - - /// - /// Flatten the nested observable collection, and observe subsequentl observable collection changes - /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The manyselector. - /// Used when an item has been replaced to determine whether child items are the same as previous children - public static IObservable> TransformMany(this IObservable> source, - Func> manyselector, - IEqualityComparer equalityComparer = null) - { - return new TransformMany(source, manyselector, equalityComparer).Run(); - } - - /// - /// Flatten the nested observable list, and observe subsequent observable collection changes - /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The manyselector. - /// Used when an item has been replaced to determine whether child items are the same as previous children - public static IObservable> TransformMany(this IObservable> source, - Func> manyselector, - IEqualityComparer equalityComparer = null) - { - return new TransformMany(source, manyselector, equalityComparer).Run(); - } - - /// - /// Selects distinct values from the source, using the specified value selector - /// - /// The type of the source. - /// The type of the destination. - /// The source. - /// The transform factory. - /// - /// - /// source - /// or - /// valueSelector - /// - public static IObservable> DistinctValues( - this IObservable> source, - Func valueSelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); - return new Distinct(source, valueSelector).Run(); - } - - /// - /// Groups the source on the value returned by group selector factory. The groupings contains an inner observable list. - /// - /// The type of the object. - /// The type of the group. - /// The source. - /// The group selector. - /// Force the grouping function to recalculate the group value. - /// For example if you have a time based grouping with values like `Last Minute', 'Last Hour', 'Today' etc regrouper is used to refresh these groupings - /// - /// - /// source - /// or - /// groupSelector - /// - public static IObservable>> GroupOn( - this IObservable> source, Func groupSelector, - IObservable regrouper = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (groupSelector == null) throw new ArgumentNullException(nameof(groupSelector)); - return new GroupOn(source, groupSelector, regrouper).Run(); - } - - /// - /// Groups the source on the value returned by group selector factory. Each update produces immuatable grouping. - /// - /// The type of the object. - /// The type of the group key. - /// The source. - /// The group selector key. - /// Force the grouping function to recalculate the group value. - /// For example if you have a time based grouping with values like `Last Minute', 'Last Hour', 'Today' etc regrouper is used to refresh these groupings - /// - /// - /// - /// source - /// or - /// groupSelectorKey - /// - public static IObservable>> GroupWithImmutableState - (this IObservable> source, - Func groupSelectorKey, - IObservable regrouper = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (groupSelectorKey == null) throw new ArgumentNullException(nameof(groupSelectorKey)); - - return new GroupOnImmutable(source, groupSelectorKey, regrouper).Run(); - } - - - /// - /// Groups the source using the property specified by the property selector. The resulting groupings contains an inner observable list. - /// Groups are re-applied when the property value changed. - /// When there are likely to be a large number of group property changes specify a throttle to improve performance - /// - /// The type of the object. - /// The type of the group. - /// The source. - /// The property selector used to group the items - /// The property changed throttle. - /// The scheduler. - /// - /// - /// - public static IObservable>> GroupOnProperty( - this IObservable> source, - Expression> propertySelector, - TimeSpan? propertyChangedThrottle = null, - IScheduler scheduler = null) - where TObject : INotifyPropertyChanged - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertySelector == null) throw new ArgumentNullException(nameof(propertySelector)); - return - new GroupOnProperty(source, propertySelector, propertyChangedThrottle, scheduler).Run(); - } - - /// - /// Groups the source using the property specified by the property selector. The resulting groupings are immutable. - /// Groups are re-applied when the property value changed. - /// When there are likely to be a large number of group property changes specify a throttle to improve performance - /// - /// The type of the object. - /// The type of the group. - /// The source. - /// The property selector used to group the items - /// The property changed throttle. - /// The scheduler. - /// - /// - /// - public static IObservable>> GroupOnPropertyWithImmutableState(this IObservable> source, - Expression> propertySelector, - TimeSpan? propertyChangedThrottle = null, - IScheduler scheduler = null) - where TObject : INotifyPropertyChanged - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertySelector == null) throw new ArgumentNullException(nameof(propertySelector)); - return new GroupOnPropertyWithImmutableState(source, propertySelector, propertyChangedThrottle, scheduler).Run(); - } - - /// - /// Prevents an empty notification - /// - /// - /// The source. - /// - /// source - public static IObservable> NotEmpty(this IObservable> source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Where(s => s.Count != 0); - } - - /// - /// Clones the target list as a side effect of the stream - /// - /// - /// The source. - /// - /// - /// source - public static IObservable> Clone(this IObservable> source, IList target) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Do(target.Clone); - } - - #endregion - - #region Sort - - /// - /// Sorts the sequence using the specified comparer. - /// - /// - /// The source. - /// The comparer used for sorting - /// For improved performance, specify SortOptions.UseBinarySearch. This can only be used when the values which are sorted on are immutable - /// Since sorting can be slow for large record sets, the reset threshold is used to force the list re-ordered - /// OnNext of this observable causes data to resort. This is required when the value which is sorted on mutable - /// An observable comparer used to change the comparer on which the sorted list i - /// - /// source - /// or - /// comparer - public static IObservable> Sort(this IObservable> source, - IComparer comparer, - SortOptions options = SortOptions.None, - IObservable resort = null, - IObservable> comparerChanged = null, - int resetThreshold = 50) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (comparer == null) throw new ArgumentNullException(nameof(comparer)); - - return new Sort(source, comparer, options, resort, comparerChanged, resetThreshold).Run(); - } - - /// - /// Sorts the sequence using the specified observable comparer. - /// - /// - /// The source. - /// For improved performance, specify SortOptions.UseBinarySearch. This can only be used when the values which are sorted on are immutable - /// Since sorting can be slow for large record sets, the reset threshold is used to force the list re-ordered - /// OnNext of this observable causes data to resort. This is required when the value which is sorted on mutable - /// An observable comparer used to change the comparer on which the sorted list i - /// - /// source - /// or - /// comparer - public static IObservable> Sort(this IObservable> source, - IObservable> comparerChanged, - SortOptions options = SortOptions.None, - IObservable resort = null, - int resetThreshold = 50) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (comparerChanged == null) throw new ArgumentNullException(nameof(comparerChanged)); - - return new Sort(source, null, options, resort, comparerChanged, resetThreshold).Run(); - } - - #endregion - - #region Item operators - - /// - /// Provides a call back for each item change. - /// - /// The type of the object. - /// The source. - /// The action. - /// - /// - /// - public static IObservable> ForEachChange( - [NotNull] this IObservable> source, - [NotNull] Action> action) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (action == null) throw new ArgumentNullException(nameof(action)); - return source.Do(changes => changes.ForEach(action)); - } - - /// - /// Provides a call back for each item change. - /// - /// Range changes are flattened, so there is only need to check for Add, Replace, Remove and Clear - /// - /// The type of the object. - /// The source. - /// The action. - /// - /// - /// - public static IObservable> ForEachItemChange( - [NotNull] this IObservable> source, - [NotNull] Action> action) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (action == null) throw new ArgumentNullException(nameof(action)); - return source.Do(changes => changes.Flatten().ForEach(action)); - } - - /// - /// Dynamically merges the observable which is selected from each item in the stream, and unmerges the item - /// when it is no longer part of the stream. - /// - /// The type of the object. - /// The type of the destination. - /// The source. - /// The observable selector. - /// - /// source - /// or - /// observableSelector - public static IObservable MergeMany( - [NotNull] this IObservable> source, - [NotNull] Func> observableSelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); - - return new MergeMany(source, observableSelector).Run(); - } - - /// - /// Watches each item in the collection and notifies when any of them has changed - /// - /// - /// The type of the value. - /// The source. - /// The property accessor. - /// if set to true [notify on initial value]. - /// - /// - /// - public static IObservable WhenValueChanged( - [NotNull] this IObservable> source, - [NotNull] Expression> propertyAccessor, - bool notifyOnInitialValue = true) - where TObject : INotifyPropertyChanged - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); - - var factory = propertyAccessor.GetFactory(); - return source.MergeMany(t => factory(t, notifyOnInitialValue).Select(pv=>pv.Value)); - } - - /// - /// Watches each item in the collection and notifies when any of them has changed - /// - /// - /// The type of the value. - /// The source. - /// The property accessor. - /// if set to true [notify on initial value]. - /// - /// - /// - public static IObservable> WhenPropertyChanged( - [NotNull] this IObservable> source, - [NotNull] Expression> propertyAccessor, - bool notifyOnInitialValue = true) - where TObject : INotifyPropertyChanged - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); - - var factory = propertyAccessor.GetFactory(); - return source.MergeMany(t => factory(t, notifyOnInitialValue)); - } - - /// - /// Watches each item in the collection and notifies when any of them has changed - /// - /// The type of the object. - /// The source. - /// specify properties to Monitor, or omit to monitor all property changes - /// - /// - /// - public static IObservable WhenAnyPropertyChanged([NotNull] this IObservable> source, params string[] propertiesToMonitor) - where TObject : INotifyPropertyChanged - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.MergeMany(t => t.WhenAnyPropertyChanged(propertiesToMonitor)); - } - - /// - /// Subscribes to each item when it is added to the stream and unsubcribes when it is removed. All items will be unsubscribed when the stream is disposed - /// - /// The type of the object. - /// The source. - /// The subsription function - /// - /// source - /// or - /// subscriptionFactory - /// - /// Subscribes to each item when it is added or updates and unsubcribes when it is removed - /// - public static IObservable> SubscribeMany(this IObservable> source, - Func subscriptionFactory) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (subscriptionFactory == null) throw new ArgumentNullException(nameof(subscriptionFactory)); - return new SubscribeMany(source, subscriptionFactory).Run(); - } - - /// - /// Disposes each item when no longer required. - /// - /// Individual items are disposed when removed or replaced. All items - /// are disposed when the stream is disposed - /// - /// - /// - /// The type of the object. - /// The source. - /// A continuation of the original stream - /// source - public static IObservable> DisposeMany(this IObservable> source) - { - return source.OnItemRemoved(t => - { - var d = t as IDisposable; - d?.Dispose(); - }); - } - - /// - /// Callback for each item as and when it is being removed from the stream - /// - /// The type of the object. - /// The source. - /// The remove action. - /// - /// - /// source - /// or - /// removeAction - /// - public static IObservable> OnItemRemoved(this IObservable> source, - Action removeAction) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (removeAction == null) throw new ArgumentNullException(nameof(removeAction)); - - return new OnBeingRemoved(source, removeAction).Run(); - } - - /// - /// Callback for each item as and when it is being added to the stream - /// - /// - /// The source. - /// The add action. - /// - public static IObservable> OnItemAdded(this IObservable> source, - Action addAction) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (addAction == null) throw new ArgumentNullException(nameof(addAction)); - return new OnBeingAdded(source, addAction).Run(); - } - - #endregion - - #region Reason filtering - - /// - /// Includes changes for the specified reasons only - /// - /// - /// The source. - /// The reasons. - /// - /// Must enter at least 1 reason - public static IObservable> WhereReasonsAre(this IObservable> source, - params ListChangeReason[] reasons) - { - if (reasons.Length == 0) - throw new ArgumentException("Must enter at least 1 reason", nameof(reasons)); - - var matches = new HashSet(reasons); - return source.Select(changes => - { - var filtered = changes.Where(change => matches.Contains(change.Reason)).YieldWithoutIndex(); - return new ChangeSet(filtered); - }).NotEmpty(); - } - - /// - /// Excludes updates for the specified reasons - /// - /// - /// The source. - /// The reasons. - /// - /// Must enter at least 1 reason - public static IObservable> WhereReasonsAreNot(this IObservable> source, - params ListChangeReason[] reasons) - { - if (reasons.Length == 0) - throw new ArgumentException("Must enter at least 1 reason", nameof(reasons)); - - var matches = new HashSet(reasons); - return source.Select(updates => - { - var filtered = updates.Where(u => !matches.Contains(u.Reason)).YieldWithoutIndex(); - return new ChangeSet(filtered); - }).NotEmpty(); - } - - #endregion - - #region Buffering - - /// - /// Buffers changes for an intial period only. After the period has elapsed, not further buffering occurs. - /// - /// The source changeset - /// The period to buffer, measure from the time that the first item arrives - /// The scheduler to buffer on - public static IObservable> BufferInitial(this IObservable> source, TimeSpan initalBuffer, IScheduler scheduler = null) - { - return source.DeferUntilLoaded().Publish(shared => - { - var initial = shared.Buffer(initalBuffer, scheduler ?? Scheduler.Default) - .FlattenBufferResult() - .Take(1); - - return initial.Concat(shared); - }); - } - - /// - /// Convert the result of a buffer operation to a change set - /// - /// - /// The source. - /// - public static IObservable> FlattenBufferResult(this IObservable>> source) - { - return source - .Where(x => x.Count != 0) - .Select(updates => new ChangeSet(updates.SelectMany(u => u))); - } - - /// - /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. - /// When a resume signal has been received the batched updates will be fired. - /// - /// The type of the object. - /// The source. - /// When true, observable begins to buffer and when false, window closes and buffered result if notified - /// The scheduler. - /// - /// source - public static IObservable> BufferIf([NotNull] this IObservable> source, - [NotNull] IObservable pauseIfTrueSelector, - IScheduler scheduler = null) - { - return BufferIf(source, pauseIfTrueSelector, false, scheduler); - } - - /// - /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. - /// When a resume signal has been received the batched updates will be fired. - /// - /// The type of the object. - /// The source. - /// When true, observable begins to buffer and when false, window closes and buffered result if notified - /// if set to true [intial pause state]. - /// The scheduler. - /// - /// source - public static IObservable> BufferIf([NotNull] this IObservable> source, - [NotNull] IObservable pauseIfTrueSelector, - bool intialPauseState = false, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (pauseIfTrueSelector == null) throw new ArgumentNullException(nameof(pauseIfTrueSelector)); - return BufferIf(source, pauseIfTrueSelector, intialPauseState, null, scheduler); - } - - /// - /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. - /// When a resume signal has been received the batched updates will be fired. - /// - /// The type of the object. - /// The source. - /// When true, observable begins to buffer and when false, window closes and buffered result if notified - /// Specify a time to ensure the buffer window does not stay open for too long - /// The scheduler. - /// - /// source - public static IObservable> BufferIf(this IObservable> source, - IObservable pauseIfTrueSelector, - TimeSpan? timeOut = null, - IScheduler scheduler = null) - { - return BufferIf(source, pauseIfTrueSelector, false, timeOut, scheduler); - } - - /// - /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. - /// When a resume signal has been received the batched updates will be fired. - /// - /// The type of the object. - /// The source. - /// When true, observable begins to buffer and when false, window closes and buffered result if notified - /// if set to true [intial pause state]. - /// Specify a time to ensure the buffer window does not stay open for too long - /// The scheduler. - /// - /// source - public static IObservable> BufferIf(this IObservable> source, - IObservable pauseIfTrueSelector, - bool intialPauseState = false, - TimeSpan? timeOut = null, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (pauseIfTrueSelector == null) throw new ArgumentNullException(nameof(pauseIfTrueSelector)); - return new BufferIf(source, pauseIfTrueSelector, intialPauseState, timeOut, scheduler).Run(); - } - - /// - /// The latest copy of the cache is exposed for querying after each modification to the underlying data - /// - /// The type of the object. - /// The type of the destination. - /// The source. - /// The result selector. - /// - /// - /// source - /// or - /// resultSelector - /// - public static IObservable QueryWhenChanged( - this IObservable> source, - Func, TDestination> resultSelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return source.QueryWhenChanged().Select(resultSelector); - } - - /// - /// The latest copy of the cache is exposed for querying i) after each modification to the underlying data ii) upon subscription - /// - /// The type of the object. - /// The source. - /// - /// source - public static IObservable> QueryWhenChanged([NotNull] this IObservable> source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return new QueryWhenChanged(source).Run(); - } - - /// - /// Converts the changeset into a fully formed collection. Each change in the source results in a new collection - /// - /// The type of the object. - /// The source. - /// - public static IObservable> ToCollection(this IObservable> source) - { - return source.QueryWhenChanged(items => items); - } - - /// - /// Converts the changeset into a fully formed sorted collection. Each change in the source results in a new sorted collection - /// - /// The type of the object. - /// The sort key - /// The source. - /// The sort function - /// The sort order. Defaults to ascending - /// - public static IObservable> ToSortedCollection(this IObservable> source, - Func sort, SortDirection sortOrder = SortDirection.Ascending) - { - return source.QueryWhenChanged(query => sortOrder == SortDirection.Ascending - ? new ReadOnlyCollectionLight(query.OrderBy(sort)) - : new ReadOnlyCollectionLight(query.OrderByDescending(sort))); - } - - /// - /// Converts the changeset into a fully formed sorted collection. Each change in the source results in a new sorted collection - /// - /// The type of the object. - /// The source. - /// The sort comparer - /// - public static IObservable> ToSortedCollection(this IObservable> source, - IComparer comparer) - { - return source.QueryWhenChanged(query => - { - var items = query.AsList(); - items.Sort(comparer); - return new ReadOnlyCollectionLight(items); - }); - } - - - /// - /// Defer the subscribtion until loaded and skip initial changeset - /// - /// The type of the object. - /// The source. - /// - /// source - public static IObservable> SkipInitial(this IObservable> source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.DeferUntilLoaded().Skip(1); - } - - /// - /// Defer the subscription until the stream has been inflated with data - /// - /// The type of the object. - /// The source. - /// - public static IObservable> DeferUntilLoaded([NotNull] this IObservable> source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return new DeferUntilLoaded(source).Run(); - } - - /// - /// Defer the subscription until the cache has been inflated with data - /// - /// The type of the object. - /// The source. - /// - public static IObservable> DeferUntilLoaded(this IObservableList source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Connect().DeferUntilLoaded(); - } - - #endregion - - #region Virtualisation / Paging - - /// - /// Virtualises the source using parameters provided via the requests observable - /// - /// - /// The source. - /// The requests. - /// - public static IObservable> Virtualise([NotNull] this IObservable> source, - [NotNull] IObservable requests) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (requests == null) throw new ArgumentNullException(nameof(requests)); - return new Virtualiser(source, requests).Run(); - } - - /// - /// Limits the size of the result set to the specified number of items - /// - /// - /// The source. - /// The number of items. - /// - public static IObservable> Top([NotNull] this IObservable> source, - int numberOfItems) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (numberOfItems <= 0) - throw new ArgumentOutOfRangeException(nameof(numberOfItems), - "Number of items should be greater than zero"); - - return source.Virtualise(Observable.Return(new VirtualRequest(0, numberOfItems))); - } - - - /// - /// Applies paging to the the data source - /// - /// - /// The source. - /// Observable to control page requests - /// - public static IObservable> Page([NotNull] this IObservable> source, - [NotNull] IObservable requests) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (requests == null) throw new ArgumentNullException(nameof(requests)); - return new Pager(source, requests).Run(); - } - - #endregion - - #region Expiry / size limiter - - /// - /// Limits the size of the source cache to the specified limit. - /// Notifies which items have been removed from the source list. - /// - /// - /// The source. - /// The size limit. - /// The scheduler. - /// - /// sizeLimit cannot be zero - /// source - /// sizeLimit cannot be zero - public static IObservable> LimitSizeTo([NotNull] this ISourceList source, int sizeLimit, - IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (sizeLimit <= 0) throw new ArgumentException("sizeLimit cannot be zero", nameof(sizeLimit)); - - var locker = new object(); - var limiter = new LimitSizeTo(source, sizeLimit, scheduler ?? Scheduler.Default, locker); - - return limiter.Run().Synchronize(locker).Do(source.RemoveMany); - } - - /// - /// Removes items from the cache according to the value specified by the time selector function - /// - /// - /// The source. - /// Selector returning when to expire the item. Return null for non-expiring item - /// The scheduler - /// - /// - /// - public static IObservable> ExpireAfter([NotNull] this ISourceList source, - [NotNull] Func timeSelector, IScheduler scheduler = null) - { - return source.ExpireAfter(timeSelector, null, scheduler); - } - - /// - /// Removes items from the cache according to the value specified by the time selector function - /// - /// - /// The source. - /// Selector returning when to expire the item. Return null for non-expiring item - /// Enter the polling interval to optimise expiry timers, if ommited 1 timer is created for each unique expiry time - /// The scheduler - /// - /// - /// - public static IObservable> ExpireAfter([NotNull] this ISourceList source, - [NotNull] Func timeSelector, TimeSpan? pollingInterval = null, IScheduler scheduler = null) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (timeSelector == null) throw new ArgumentNullException(nameof(timeSelector)); - - var locker = new object(); - var limiter = new ExpireAfter(source, timeSelector, pollingInterval, scheduler ?? Scheduler.Default, - locker); - - return limiter.Run().Synchronize(locker).Do(source.RemoveMany); - } - - #endregion - - #region Logical collection operators - - /// - /// Apply a logical Or operator between the collections. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> Or([NotNull] this ICollection>> sources) - { - return sources.Combine(CombineOperator.Or); - } - - /// - /// Apply a logical Or operator between the collections. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// The others. - /// - public static IObservable> Or([NotNull] this IObservable> source, - params IObservable>[] others) - { - return source.Combine(CombineOperator.Or, others); - } - - /// - /// Dynamically apply a logical Or operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> Or( - [NotNull] this IObservableList>> sources) - { - return sources.Combine(CombineOperator.Or); - } - - /// - /// Dynamically apply a logical Or operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> Or([NotNull] this IObservableList> sources) - { - return sources.Combine(CombineOperator.Or); - } - - /// - /// Dynamically apply a logical Or operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> Or([NotNull] this IObservableList> sources) - { - return sources.Combine(CombineOperator.Or); - } - - /// - /// Apply a logical Xor operator between the collections. - /// Items which are only in one of the sources are included in the result - /// - /// - /// The source. - /// The others. - /// - public static IObservable> Xor([NotNull] this IObservable> source, - params IObservable>[] others) - { - return source.Combine(CombineOperator.Xor, others); - } - - /// - /// Apply a logical Xor operator between the collections. - /// Items which are only in one of the sources are included in the result - /// - /// - /// The sources. - /// > - public static IObservable> Xor([NotNull] this ICollection>> sources) - { - return sources.Combine(CombineOperator.Xor); - } - - /// - /// Dynamically apply a logical Xor operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> Xor( - [NotNull] this IObservableList>> sources) - { - return sources.Combine(CombineOperator.Xor); - } - - /// - /// Dynamically apply a logical Xor operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> Xor([NotNull] this IObservableList> sources) - { - return sources.Combine(CombineOperator.Xor); - } - - /// - /// Dynamically apply a logical Xor operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> Xor([NotNull] this IObservableList> sources) - { - return sources.Combine(CombineOperator.Xor); - } - - /// - /// Apply a logical And operator between the collections. - /// Items which are in all of the sources are included in the result - /// - /// - /// The source. - /// The others. - /// - public static IObservable> And([NotNull] this IObservable> source, - params IObservable>[] others) - { - return source.Combine(CombineOperator.And, others); - } - - /// - /// Apply a logical And operator between the collections. - /// Items which are in all of the sources are included in the result - /// - /// - /// The sources. - /// > - public static IObservable> And([NotNull] this ICollection>> sources) - { - return sources.Combine(CombineOperator.And); - } - - /// - /// Dynamically apply a logical And operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> And( - [NotNull] this IObservableList>> sources) - { - return sources.Combine(CombineOperator.And); - } - - /// - /// Dynamically apply a logical And operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> And([NotNull] this IObservableList> sources) - { - return sources.Combine(CombineOperator.And); - } - - /// - /// Dynamically apply a logical And operator between the items in the outer observable list. - /// Items which are in any of the sources are included in the result - /// - /// - /// The source. - /// - public static IObservable> And([NotNull] this IObservableList> sources) - { - return sources.Combine(CombineOperator.And); - } - - /// - /// Apply a logical Except operator between the collections. - /// Items which are in the source and not in the others are included in the result - /// - /// - /// The source. - /// The others. - /// - public static IObservable> Except([NotNull] this IObservable> source, - params IObservable>[] others) - { - return source.Combine(CombineOperator.Except, others); - } - - /// - /// Apply a logical Except operator between the collections. - /// Items which are in the source and not in the others are included in the result - /// - /// - /// The sources. - /// > - public static IObservable> Except( - [NotNull] this ICollection>> sources) - { - return sources.Combine(CombineOperator.Except); - } - - /// - /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. - /// - /// - /// The source. - /// - public static IObservable> Except( - [NotNull] this IObservableList>> sources) - { - return sources.Combine(CombineOperator.Except); - } - - /// - /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. - /// - /// - /// The source. - /// - public static IObservable> Except([NotNull] this IObservableList> sources) - { - return sources.Combine(CombineOperator.Except); - } - - /// - /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. - /// - /// - /// The source. - /// - public static IObservable> Except([NotNull] this IObservableList> sources) - { - return sources.Combine(CombineOperator.Except); - } - - private static IObservable> Combine( - [NotNull] this ICollection>> sources, - CombineOperator type) - { - if (sources == null) throw new ArgumentNullException(nameof(sources)); - - return new Combiner(sources, type).Run(); - } - - private static IObservable> Combine([NotNull] this IObservable> source, - CombineOperator type, - params IObservable>[] others) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (others.Length == 0) - throw new ArgumentException("Must be at least one item to combine with", nameof(others)); - - var items = source.EnumerateOne().Union(others).ToList(); - return new Combiner(items, type).Run(); - } - - private static IObservable> Combine([NotNull] this IObservableList> sources, - CombineOperator type) - { - if (sources == null) throw new ArgumentNullException(nameof(sources)); - - return Observable.Create>(observer => - { - var changesSetList = sources.Connect().Transform(s => s.Connect()).AsObservableList(); - var subscriber = changesSetList.Combine(type).SubscribeSafe(observer); - return new CompositeDisposable(changesSetList, subscriber); - }); - } - - private static IObservable> Combine([NotNull] this IObservableList> sources, - CombineOperator type) - { - if (sources == null) throw new ArgumentNullException(nameof(sources)); - - return Observable.Create>(observer => - { - var changesSetList = sources.Connect().Transform(s => s.Connect()).AsObservableList(); - var subscriber = changesSetList.Combine(type).SubscribeSafe(observer); - return new CompositeDisposable(changesSetList, subscriber); - }); - } - - private static IObservable> Combine( - [NotNull] this IObservableList>> sources, CombineOperator type) - { - if (sources == null) throw new ArgumentNullException(nameof(sources)); - return new DynamicCombiner(sources, type).Run(); - } - - #endregion - - #region Switch - - /// - /// Transforms an observable sequence of observable lists into a single sequence - /// producing values only from the most recent observable sequence. - /// Each time a new inner observable sequence is received, unsubscribe from the - /// previous inner observable sequence and clear the existing result set - /// - /// The type of the object. - /// The source. - /// - /// The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. - /// - /// - /// is null. - public static IObservable> Switch(this IObservable> sources) - { - if (sources == null) throw new ArgumentNullException(nameof(sources)); - return sources.Select(cache => cache.Connect()).Switch(); - } - - /// - /// Transforms an observable sequence of observable changes sets into an observable sequence - /// producing values only from the most recent observable sequence. - /// Each time a new inner observable sequence is received, unsubscribe from the - /// previous inner observable sequence and clear the existing resukt set - /// - /// The type of the object. - /// The source. - /// - /// The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. - /// - /// - /// is null. - public static IObservable> Switch(this IObservable>> sources) - { - if (sources == null) throw new ArgumentNullException(nameof(sources)); - return new Switch(sources).Run(); - } - - #endregion - - - #region Start with - - /// - /// Prepends an empty changeset to the source - /// - public static IObservable> StartWithEmpty(this IObservable> source) - { - return source.StartWith(ChangeSet.Empty); - } - - - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Linq.Expressions; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Threading.Tasks; +using DynamicData.Annotations; +using DynamicData.Binding; +using DynamicData.Cache.Internal; +using DynamicData.Kernel; +using DynamicData.List.Internal; +using DynamicData.List.Linq; + +// ReSharper disable once CheckNamespace + +namespace DynamicData +{ + /// + /// Extensions for ObservableList + /// + public static class ObservableListEx + { + #region Populate change set from standard rx observable + + /// + /// Converts the observable to an observable changeset. + /// + /// The type of the object. + /// The source. + /// The scheduler (only used for time expiry). + /// + /// source + /// or + /// keySelector + public static IObservable> ToObservableChangeSet(this IObservable source, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + return ToObservableChangeSet(source, null, -1, scheduler); + } + + /// + /// Converts the observable to an observable changeset, allowing time expiry to be specified + /// + /// The type of the object. + /// The source. + /// Specify on a per object level the maximum time before an object expires from a cache + /// The scheduler (only used for time expiry). + /// + /// source + /// or + /// keySelector + public static IObservable> ToObservableChangeSet(this IObservable source, + Func expireAfter, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (expireAfter == null) throw new ArgumentNullException(nameof(expireAfter)); + + return ToObservableChangeSet(source, expireAfter, -1, scheduler); + } + + /// + /// Converts the observable to an observable changeset, with a specified limit of how large the list can be. + /// + /// The type of the object. + /// The source. + /// Remove the oldest items when the size has reached this limit + /// The scheduler (only used for time expiry). + /// + /// source + /// or + /// keySelector + public static IObservable> ToObservableChangeSet(this IObservable source, + int limitSizeTo, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return ToObservableChangeSet(source, null, limitSizeTo, scheduler); + } + + /// + /// Converts the observable to an observable changeset, allowing size and time limit to be specified + /// + /// The type of the object. + /// The source. + /// Specify on a per object level the maximum time before an object expires from a cache + /// Remove the oldest items when the size has reached this limit + /// The scheduler (only used for time expiry). + /// + /// source + /// or + /// keySelector + public static IObservable> ToObservableChangeSet(this IObservable source, + Func expireAfter, + int limitSizeTo, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return new ToObservableChangeSet(source, expireAfter, limitSizeTo, scheduler).Run(); + } + + /// + /// Converts the observable to an observable changeset. + /// + /// The type of the object. + /// The source. + /// The scheduler (only used for time expiry). + /// + /// source + /// or + /// keySelector + public static IObservable> ToObservableChangeSet(this IObservable> source, + IScheduler scheduler = null) + { + return ToObservableChangeSet(source, null, -1, scheduler); + } + + /// + /// Converts the observable to an observable changeset, allowing size and time limit to be specified + /// + /// The type of the object. + /// The source. + /// Remove the oldest items when the size has reached this limit + /// The scheduler (only used for time expiry). + /// + /// source + /// or + /// keySelector + public static IObservable> ToObservableChangeSet(this IObservable> source, + int limitSizeTo, + IScheduler scheduler = null) + { + return ToObservableChangeSet(source, null, limitSizeTo, scheduler); + } + + /// + /// Converts the observable to an observable changeset, allowing size to be specified + /// + /// The type of the object. + /// The source. + /// Specify on a per object level the maximum time before an object expires from a cache + /// The scheduler (only used for time expiry). + /// + /// source + /// or + /// keySelector + public static IObservable> ToObservableChangeSet(this IObservable> source, + Func expireAfter, + IScheduler scheduler = null) + { + return ToObservableChangeSet(source, expireAfter, 0, scheduler); + } + + /// + /// Converts the observable to an observable changeset, allowing size and time limit to be specified + /// + /// The type of the object. + /// The source. + /// Specify on a per object level the maximum time before an object expires from a cache + /// Remove the oldest items when the size has reached this limit + /// The scheduler (only used for time expiry). + /// + /// source + /// or + /// keySelector + public static IObservable> ToObservableChangeSet(this IObservable> source, + Func expireAfter, + int limitSizeTo, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return new ToObservableChangeSet(source, expireAfter, limitSizeTo, scheduler).Run(); + } + + #endregion + + #region Auto Refresh + + /// + /// Automatically refresh downstream operators when any property changes. + /// + /// The source observable + /// Batch up changes by specifying the buffer. This greatly increases performance when many elements have sucessive property changes + /// When observing on multiple property changes, apply a throttle to prevent excessive refesh invocations + /// The scheduler + /// An observable change set with additional refresh changes + public static IObservable> AutoRefresh(this IObservable> source, + TimeSpan? changeSetBuffer = null, + TimeSpan? propertyChangeThrottle = null, + IScheduler scheduler = null) + where TObject : INotifyPropertyChanged + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + return source.AutoRefreshOnObservable(t => + { + if (propertyChangeThrottle == null) + return t.WhenAnyPropertyChanged(); + + return t.WhenAnyPropertyChanged() + .Throttle(propertyChangeThrottle.Value, scheduler ?? Scheduler.Default); + + }, changeSetBuffer, scheduler); + } + + /// + /// Automatically refresh downstream operators when properties change. + /// + /// The source observable + /// Specify a property to observe changes. When it changes a Refresh is invoked + /// Batch up changes by specifying the buffer. This greatly increases performance when many elements have sucessive property changes + /// When observing on multiple property changes, apply a throttle to prevent excessive refesh invocations + /// The scheduler + /// An observable change set with additional refresh changes + public static IObservable> AutoRefresh(this IObservable> source, + Expression> propertyAccessor, + TimeSpan? changeSetBuffer = null, + TimeSpan? propertyChangeThrottle = null, + IScheduler scheduler = null) + where TObject : INotifyPropertyChanged + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + + return source.AutoRefreshOnObservable(t => + { + if (propertyChangeThrottle == null) + return t.WhenPropertyChanged(propertyAccessor, false); + + return t.WhenPropertyChanged(propertyAccessor,false) + .Throttle(propertyChangeThrottle.Value, scheduler ?? Scheduler.Default); + + }, changeSetBuffer, scheduler); + } + + /// + /// Automatically refresh downstream operator. The refresh is triggered when the observable receives a notification + /// + /// The source observable change set + /// An observable which acts on items within the collection and produces a value when the item should be refreshed + /// Batch up changes by specifying the buffer. This greatly increases performance when many elements require a refresh + /// The scheduler + /// An observable change set with additional refresh changes + public static IObservable> AutoRefreshOnObservable(this IObservable> source, + Func> reevaluator, + TimeSpan? changeSetBuffer = null, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (reevaluator == null) throw new ArgumentNullException(nameof(reevaluator)); + return new AutoRefresh(source, reevaluator, changeSetBuffer, scheduler).Run(); + } + + + /// + /// Supress refresh notifications + /// + /// The source observable change set + /// + public static IObservable> SupressRefresh(this IObservable> source) + { + return source.WhereReasonsAreNot(ListChangeReason.Refresh); + } + + + #endregion + + #region Conversion + + /// + /// Removes the index from all changes. + /// + /// NB: This operator has been introduced as a temporary fix for creating an Or operator using merge many. + /// + /// The type of the object. + /// The source. + /// + /// + public static IObservable> RemoveIndex([NotNull] this IObservable> source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Select(changes => new ChangeSet(changes.YieldWithoutIndex())); + } + + /// + /// Adds a key to the change set result which enables all observable cache features of dynamic data + /// + /// + /// All indexed changes are dropped i.e. sorting is not supported by this function + /// + /// The type of object. + /// The type of key. + /// The source. + /// The key selector. + /// + /// + /// + public static IObservable> AddKey( + [NotNull] this IObservable> source, [NotNull] Func keySelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + return source.Select(changes => new ChangeSet(new AddKeyEnumerator(changes, keySelector))); + } + + /// + /// Convert the object using the sepcified conversion function. + /// + /// This is a lighter equivalent of Transform and is designed to be used with non-disposable objects + /// + /// The type of the object. + /// The type of the destination. + /// The source. + /// The conversion factory. + /// + /// + /// + [Obsolete("Prefer Cast as it is does the same thing but is semantically correct")] + public static IObservable> Convert( + [NotNull] this IObservable> source, + [NotNull] Func conversionFactory) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (conversionFactory == null) throw new ArgumentNullException(nameof(conversionFactory)); + return source.Select(changes => changes.Transform(conversionFactory)); + } + + + + /// + /// Cast the underlying type of an object. Use before a Cast function + /// + /// + /// The source. + /// + public static IObservable> CastToObject(this IObservable> source) + { + return source.Select(changes => + { + var items = changes.Transform(t => (object)t); + return new ChangeSet(items); + }); + } + + /// + /// Cast the changes to another form + /// + /// The type of the destination. + /// The source. + /// + /// + /// + public static IObservable> Cast([NotNull] this IObservable> source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Select(changes => changes.Transform(t=>(TDestination)t)); + } + + /// + /// Cast the changes to another form + /// + /// Alas, I had to add the converter due to type inference issues. The converter can be avoided by CastToObject() first + /// + /// The type of the object. + /// The type of the destination. + /// The source. + /// The conversion factory. + /// + /// + /// + public static IObservable> Cast([NotNull] this IObservable> source, + [NotNull] Func conversionFactory) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (conversionFactory == null) throw new ArgumentNullException(nameof(conversionFactory)); + return source.Select(changes => changes.Transform(conversionFactory)); + } + + #endregion + + #region Binding + + /// + /// Binds a clone of the observable changeset to the target observable collection + /// + /// + /// The source. + /// The target collection. + /// The reset threshold. + /// + /// + /// source + /// or + /// targetCollection + /// + public static IObservable> Bind([NotNull] this IObservable> source, + [NotNull] IObservableCollection targetCollection, int resetThreshold = 25) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (targetCollection == null) throw new ArgumentNullException(nameof(targetCollection)); + + var adaptor = new ObservableCollectionAdaptor(targetCollection, resetThreshold); + return source.Adapt(adaptor); + } + + /// + /// Creates a binding to a readonly observable collection which is specified as an 'out' parameter + /// + /// + /// The source. + /// The resulting read only observable collection. + /// The reset threshold. + /// A continuation of the source stream + /// + /// + public static IObservable> Bind([NotNull] this IObservable> source, + out ReadOnlyObservableCollection readOnlyObservableCollection, int resetThreshold = 25) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + var target = new ObservableCollectionExtended(); + var result = new ReadOnlyObservableCollection(target); + var adaptor = new ObservableCollectionAdaptor(target, resetThreshold); + readOnlyObservableCollection = result; + return source.Adapt(adaptor); + } + +#if SUPPORTS_BINDINGLIST + + /// + /// Binds a clone of the observable changeset to the target observable collection + /// + /// + /// The source. + /// The target binding list + /// The reset threshold. + /// + /// source + /// or + /// targetCollection + /// + public static IObservable> Bind([NotNull] this IObservable> source, + [NotNull] BindingList bindingList, int resetThreshold = 25) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (bindingList == null) throw new ArgumentNullException(nameof(bindingList)); + + return source.Adapt(new BindingListAdaptor(bindingList, resetThreshold)); + } + +#endif + + + /// + /// Injects a side effect into a changeset observable + /// + /// + /// The source. + /// The adaptor. + /// + /// + /// source + /// or + /// adaptor + /// + public static IObservable> Adapt([NotNull] this IObservable> source, + [NotNull] IChangeSetAdaptor adaptor) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (adaptor == null) throw new ArgumentNullException(nameof(adaptor)); + + + return Observable.Create>(observer => + { + var locker = new object(); + return source + .Synchronize(locker) + .Select(changes => + { + adaptor.Adapt(changes); + return changes; + }).SubscribeSafe(observer); + }); + } + + #endregion + + #region Populate into an observable list + + /// + /// list. + /// + /// The type of the object. + /// The source. + /// The destination. + /// + /// + /// source + /// or + /// destination + /// + /// source + /// or + /// destination + public static IDisposable PopulateInto([NotNull] this IObservable> source, + [NotNull] ISourceList destination) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (destination == null) throw new ArgumentNullException(nameof(destination)); + + return source.Subscribe(changes => destination.Edit(updater => updater.Clone(changes))); + } + + /// + /// Converts the source list to an read only observable list + /// + /// + /// The source. + /// + /// source + public static IObservableList AsObservableList([NotNull] this ISourceList source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return new AnonymousObservableList(source); + } + + /// + /// Converts the source observable to an read only observable list + /// + /// + /// The source. + /// + /// source + public static IObservableList AsObservableList([NotNull] this IObservable> source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return new AnonymousObservableList(source); + } + + /// + /// List equivalent to Publish().RefCount(). The source is cached so long as there is at least 1 subscriber. + /// + /// + /// The source. + /// + /// + public static IObservable> RefCount([NotNull] this IObservable> source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return new RefCount(source).Run(); + } + + #endregion + + #region Core List Operators + + /// + /// Filters the source using the specified valueSelector + /// + /// + /// The source. + /// The valueSelector. + /// + /// source + public static IObservable> Filter(this IObservable> source, Func predicate) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + return new Filter(source, predicate).Run(); + } + + /// + /// Filters source using the specified filter observable predicate. + /// + /// + /// The source. + /// + /// Should the filter clear and replace, or calculate a diff-set + /// + /// source + /// or + /// filterController + public static IObservable> Filter([NotNull] this IObservable> source, [NotNull] IObservable> predicate, ListFilterPolicy filterPolicy = ListFilterPolicy.CalculateDiff) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + + return new Filter(source, predicate, filterPolicy).Run(); + } + + + /// + /// Filters source on the specified property using the specified predicate. + /// + /// The filter will automatically reapply when a property changes + /// + /// The type of the object. + /// The type of the property. + /// The source. + /// The property selector. When the property changes the filter specified will be re-evaluated + /// A predicate based on the object which contains the changed property + /// The property changed throttle. + /// The scheduler used when throttling + /// + /// + /// + [Obsolete("Use AutoRefresh(), followed by Filter() instead")] + public static IObservable> FilterOnProperty(this IObservable> source, + Expression> propertySelector, + Func predicate, + TimeSpan? propertyChangedThrottle = null, + IScheduler scheduler = null) where TObject : INotifyPropertyChanged + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (propertySelector == null) throw new ArgumentNullException(nameof(propertySelector)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + return new FilterOnProperty(source, propertySelector, predicate, propertyChangedThrottle, scheduler).Run(); + } + + /// + /// Filters source on the specified observable property using the specified predicate. + /// + /// The filter will automatically reapply when a property changes + /// + /// The type of the object. + /// The source. + /// The filter property selector. When the observable changes the filter will be re-evaluated + /// The property changed throttle. + /// The scheduler used when throttling + /// + /// + /// + public static IObservable> FilterOnObservable(this IObservable> source, + Func> objectFilterObservable, + TimeSpan? propertyChangedThrottle = null, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return new FilterOnObservable(source, objectFilterObservable, propertyChangedThrottle, scheduler).Run(); + } + + /// + /// Reverse sort of the changset + /// + /// + /// The source. + /// + /// + /// source + /// or + /// comparer + /// + public static IObservable> Reverse(this IObservable> source) + { + var reverser = new Reverser(); + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Select(changes => new ChangeSet(reverser.Reverse(changes))); + } + + /// + /// Projects each update item to a new form using the specified transform function + /// + /// The type of the source. + /// The type of the destination. + /// The source. + /// The transform factory. + /// Should a new transform be applied when a refresh event is received + /// + /// + /// source + /// or + /// valueSelector + /// + public static IObservable> Transform(this IObservable> source, + Func transformFactory, + bool transformOnRefresh = false) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + return source.Transform((t, previous, idx) => transformFactory(t), transformOnRefresh); + } + + /// + /// Projects each update item to a new form using the specified transform function + /// + /// The type of the source. + /// The type of the destination. + /// The source. + /// The transform function + /// Should a new transform be applied when a refresh event is received + /// A an observable changeset of the transformed object + /// + /// source + /// or + /// valueSelector + /// + public static IObservable> Transform(this IObservable> source, + Func transformFactory, + bool transformOnRefresh = false) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + + return source.Transform((t, previous, idx) => transformFactory(t,idx),transformOnRefresh); + } + + /// + /// Projects each update item to a new form using the specified transform function. + /// + /// *** Annoyingly when using this overload you will have to explicitly specify the generic type arguments as type inference fails + /// + /// The type of the source. + /// The type of the destination. + /// The source. + /// The transform function + /// Should a new transform be applied when a refresh event is received + /// A an observable changeset of the transformed object + /// + /// source + /// or + /// valueSelector + /// + public static IObservable> Transform(this IObservable> source, + Func, TDestination> transformFactory, + bool transformOnRefresh = false) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + + return source.Transform((t, previous, idx) => transformFactory(t, previous), transformOnRefresh); + } + + /// + /// Projects each update item to a new form using the specified transform function + /// + /// *** Annoyingly when using this overload you will have to explicy specify the generic type arguments as type inference fails + /// + /// The type of the source. + /// The type of the destination. + /// The source. + /// The transform factory. + /// Should a new transform be applied when a refresh event is received + /// A an observable changeset of the transformed object + /// + /// source + /// or + /// valueSelector + /// + public static IObservable> Transform(this IObservable> source, + Func, int, TDestination> transformFactory, bool transformOnRefresh = false) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + + return new Transformer(source, transformFactory, transformOnRefresh).Run(); + } + + + /// + /// Projects each update item to a new form using the specified transform function + /// + /// The type of the source. + /// The type of the destination. + /// The source. + /// The transform factory. + /// A an observable changeset of the transformed object + /// + /// source + /// or + /// valueSelector + /// + public static IObservable> TransformAsync( + this IObservable> source, Func> transformFactory) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + + return new TransformAsync(source, transformFactory).Run(); + } + + /// + /// Equivalent to a select many transform. To work, the key must individually identify each child. + /// + /// The type of the destination. + /// The type of the source. + /// The source. + /// The manyselector. + /// Used when an item has been replaced to determine whether child items are the same as previous children + /// + /// + /// source + /// or + /// manyselector + /// + public static IObservable> TransformMany( [NotNull] this IObservable> source, + [NotNull] Func> manyselector, + IEqualityComparer equalityComparer = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (manyselector == null) throw new ArgumentNullException(nameof(manyselector)); + return new TransformMany(source, manyselector, equalityComparer).Run(); + } + + /// + /// Flatten the nested observable collection, and observe subsequentl observable collection changes + /// + /// The type of the destination. + /// The type of the source. + /// The source. + /// The manyselector. + /// Used when an item has been replaced to determine whether child items are the same as previous children + public static IObservable> TransformMany( this IObservable> source, + Func> manyselector, + IEqualityComparer equalityComparer = null) + { + return new TransformMany(source,manyselector, equalityComparer).Run(); + } + + /// + /// Flatten the nested observable collection, and observe subsequentl observable collection changes + /// + /// The type of the destination. + /// The type of the source. + /// The source. + /// The manyselector. + /// Used when an item has been replaced to determine whether child items are the same as previous children + public static IObservable> TransformMany(this IObservable> source, + Func> manyselector, + IEqualityComparer equalityComparer = null) + { + return new TransformMany(source, manyselector, equalityComparer).Run(); + } + + /// + /// Flatten the nested observable list, and observe subsequent observable collection changes + /// + /// The type of the destination. + /// The type of the source. + /// The source. + /// The manyselector. + /// Used when an item has been replaced to determine whether child items are the same as previous children + public static IObservable> TransformMany(this IObservable> source, + Func> manyselector, + IEqualityComparer equalityComparer = null) + { + return new TransformMany(source, manyselector, equalityComparer).Run(); + } + + /// + /// Selects distinct values from the source, using the specified value selector + /// + /// The type of the source. + /// The type of the destination. + /// The source. + /// The transform factory. + /// + /// + /// source + /// or + /// valueSelector + /// + public static IObservable> DistinctValues( + this IObservable> source, + Func valueSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + return new Distinct(source, valueSelector).Run(); + } + + /// + /// Groups the source on the value returned by group selector factory. The groupings contains an inner observable list. + /// + /// The type of the object. + /// The type of the group. + /// The source. + /// The group selector. + /// Force the grouping function to recalculate the group value. + /// For example if you have a time based grouping with values like `Last Minute', 'Last Hour', 'Today' etc regrouper is used to refresh these groupings + /// + /// + /// source + /// or + /// groupSelector + /// + public static IObservable>> GroupOn( + this IObservable> source, Func groupSelector, + IObservable regrouper = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (groupSelector == null) throw new ArgumentNullException(nameof(groupSelector)); + return new GroupOn(source, groupSelector, regrouper).Run(); + } + + /// + /// Groups the source on the value returned by group selector factory. Each update produces immuatable grouping. + /// + /// The type of the object. + /// The type of the group key. + /// The source. + /// The group selector key. + /// Force the grouping function to recalculate the group value. + /// For example if you have a time based grouping with values like `Last Minute', 'Last Hour', 'Today' etc regrouper is used to refresh these groupings + /// + /// + /// + /// source + /// or + /// groupSelectorKey + /// + public static IObservable>> GroupWithImmutableState + (this IObservable> source, + Func groupSelectorKey, + IObservable regrouper = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (groupSelectorKey == null) throw new ArgumentNullException(nameof(groupSelectorKey)); + + return new GroupOnImmutable(source, groupSelectorKey, regrouper).Run(); + } + + + /// + /// Groups the source using the property specified by the property selector. The resulting groupings contains an inner observable list. + /// Groups are re-applied when the property value changed. + /// When there are likely to be a large number of group property changes specify a throttle to improve performance + /// + /// The type of the object. + /// The type of the group. + /// The source. + /// The property selector used to group the items + /// The property changed throttle. + /// The scheduler. + /// + /// + /// + public static IObservable>> GroupOnProperty( + this IObservable> source, + Expression> propertySelector, + TimeSpan? propertyChangedThrottle = null, + IScheduler scheduler = null) + where TObject : INotifyPropertyChanged + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (propertySelector == null) throw new ArgumentNullException(nameof(propertySelector)); + return + new GroupOnProperty(source, propertySelector, propertyChangedThrottle, scheduler).Run(); + } + + /// + /// Groups the source using the property specified by the property selector. The resulting groupings are immutable. + /// Groups are re-applied when the property value changed. + /// When there are likely to be a large number of group property changes specify a throttle to improve performance + /// + /// The type of the object. + /// The type of the group. + /// The source. + /// The property selector used to group the items + /// The property changed throttle. + /// The scheduler. + /// + /// + /// + public static IObservable>> GroupOnPropertyWithImmutableState(this IObservable> source, + Expression> propertySelector, + TimeSpan? propertyChangedThrottle = null, + IScheduler scheduler = null) + where TObject : INotifyPropertyChanged + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (propertySelector == null) throw new ArgumentNullException(nameof(propertySelector)); + return new GroupOnPropertyWithImmutableState(source, propertySelector, propertyChangedThrottle, scheduler).Run(); + } + + /// + /// Prevents an empty notification + /// + /// + /// The source. + /// + /// source + public static IObservable> NotEmpty(this IObservable> source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Where(s => s.Count != 0); + } + + /// + /// Clones the target list as a side effect of the stream + /// + /// + /// The source. + /// + /// + /// source + public static IObservable> Clone(this IObservable> source, IList target) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Do(target.Clone); + } + + #endregion + + #region Sort + + /// + /// Sorts the sequence using the specified comparer. + /// + /// + /// The source. + /// The comparer used for sorting + /// For improved performance, specify SortOptions.UseBinarySearch. This can only be used when the values which are sorted on are immutable + /// Since sorting can be slow for large record sets, the reset threshold is used to force the list re-ordered + /// OnNext of this observable causes data to resort. This is required when the value which is sorted on mutable + /// An observable comparer used to change the comparer on which the sorted list i + /// + /// source + /// or + /// comparer + public static IObservable> Sort(this IObservable> source, + IComparer comparer, + SortOptions options = SortOptions.None, + IObservable resort = null, + IObservable> comparerChanged = null, + int resetThreshold = 50) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (comparer == null) throw new ArgumentNullException(nameof(comparer)); + + return new Sort(source, comparer, options, resort, comparerChanged, resetThreshold).Run(); + } + + /// + /// Sorts the sequence using the specified observable comparer. + /// + /// + /// The source. + /// For improved performance, specify SortOptions.UseBinarySearch. This can only be used when the values which are sorted on are immutable + /// Since sorting can be slow for large record sets, the reset threshold is used to force the list re-ordered + /// OnNext of this observable causes data to resort. This is required when the value which is sorted on mutable + /// An observable comparer used to change the comparer on which the sorted list i + /// + /// source + /// or + /// comparer + public static IObservable> Sort(this IObservable> source, + IObservable> comparerChanged, + SortOptions options = SortOptions.None, + IObservable resort = null, + int resetThreshold = 50) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (comparerChanged == null) throw new ArgumentNullException(nameof(comparerChanged)); + + return new Sort(source, null, options, resort, comparerChanged, resetThreshold).Run(); + } + + #endregion + + #region Item operators + + /// + /// Provides a call back for each item change. + /// + /// The type of the object. + /// The source. + /// The action. + /// + /// + /// + public static IObservable> ForEachChange( + [NotNull] this IObservable> source, + [NotNull] Action> action) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (action == null) throw new ArgumentNullException(nameof(action)); + return source.Do(changes => changes.ForEach(action)); + } + + /// + /// Provides a call back for each item change. + /// + /// Range changes are flattened, so there is only need to check for Add, Replace, Remove and Clear + /// + /// The type of the object. + /// The source. + /// The action. + /// + /// + /// + public static IObservable> ForEachItemChange( + [NotNull] this IObservable> source, + [NotNull] Action> action) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (action == null) throw new ArgumentNullException(nameof(action)); + return source.Do(changes => changes.Flatten().ForEach(action)); + } + + /// + /// Dynamically merges the observable which is selected from each item in the stream, and unmerges the item + /// when it is no longer part of the stream. + /// + /// The type of the object. + /// The type of the destination. + /// The source. + /// The observable selector. + /// + /// source + /// or + /// observableSelector + public static IObservable MergeMany( + [NotNull] this IObservable> source, + [NotNull] Func> observableSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); + + return new MergeMany(source, observableSelector).Run(); + } + + /// + /// Watches each item in the collection and notifies when any of them has changed + /// + /// + /// The type of the value. + /// The source. + /// The property accessor. + /// if set to true [notify on initial value]. + /// + /// + /// + public static IObservable WhenValueChanged( + [NotNull] this IObservable> source, + [NotNull] Expression> propertyAccessor, + bool notifyOnInitialValue = true) + where TObject : INotifyPropertyChanged + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + + var factory = propertyAccessor.GetFactory(); + return source.MergeMany(t => factory(t, notifyOnInitialValue).Select(pv=>pv.Value)); + } + + /// + /// Watches each item in the collection and notifies when any of them has changed + /// + /// + /// The type of the value. + /// The source. + /// The property accessor. + /// if set to true [notify on initial value]. + /// + /// + /// + public static IObservable> WhenPropertyChanged( + [NotNull] this IObservable> source, + [NotNull] Expression> propertyAccessor, + bool notifyOnInitialValue = true) + where TObject : INotifyPropertyChanged + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + + var factory = propertyAccessor.GetFactory(); + return source.MergeMany(t => factory(t, notifyOnInitialValue)); + } + + /// + /// Watches each item in the collection and notifies when any of them has changed + /// + /// The type of the object. + /// The source. + /// specify properties to Monitor, or omit to monitor all property changes + /// + /// + /// + public static IObservable WhenAnyPropertyChanged([NotNull] this IObservable> source, params string[] propertiesToMonitor) + where TObject : INotifyPropertyChanged + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.MergeMany(t => t.WhenAnyPropertyChanged(propertiesToMonitor)); + } + + /// + /// Subscribes to each item when it is added to the stream and unsubcribes when it is removed. All items will be unsubscribed when the stream is disposed + /// + /// The type of the object. + /// The source. + /// The subsription function + /// + /// source + /// or + /// subscriptionFactory + /// + /// Subscribes to each item when it is added or updates and unsubcribes when it is removed + /// + public static IObservable> SubscribeMany(this IObservable> source, + Func subscriptionFactory) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (subscriptionFactory == null) throw new ArgumentNullException(nameof(subscriptionFactory)); + return new SubscribeMany(source, subscriptionFactory).Run(); + } + + /// + /// Disposes each item when no longer required. + /// + /// Individual items are disposed when removed or replaced. All items + /// are disposed when the stream is disposed + /// + /// + /// + /// The type of the object. + /// The source. + /// A continuation of the original stream + /// source + public static IObservable> DisposeMany(this IObservable> source) + { + return source.OnItemRemoved(t => + { + var d = t as IDisposable; + d?.Dispose(); + }); + } + + /// + /// Callback for each item as and when it is being removed from the stream + /// + /// The type of the object. + /// The source. + /// The remove action. + /// + /// + /// source + /// or + /// removeAction + /// + public static IObservable> OnItemRemoved(this IObservable> source, + Action removeAction) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (removeAction == null) throw new ArgumentNullException(nameof(removeAction)); + + return new OnBeingRemoved(source, removeAction).Run(); + } + + /// + /// Callback for each item as and when it is being added to the stream + /// + /// + /// The source. + /// The add action. + /// + public static IObservable> OnItemAdded(this IObservable> source, + Action addAction) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (addAction == null) throw new ArgumentNullException(nameof(addAction)); + return new OnBeingAdded(source, addAction).Run(); + } + + #endregion + + #region Reason filtering + + /// + /// Includes changes for the specified reasons only + /// + /// + /// The source. + /// The reasons. + /// + /// Must enter at least 1 reason + public static IObservable> WhereReasonsAre(this IObservable> source, + params ListChangeReason[] reasons) + { + if (reasons.Length == 0) + throw new ArgumentException("Must enter at least 1 reason", nameof(reasons)); + + var matches = new HashSet(reasons); + return source.Select(changes => + { + var filtered = changes.Where(change => matches.Contains(change.Reason)).YieldWithoutIndex(); + return new ChangeSet(filtered); + }).NotEmpty(); + } + + /// + /// Excludes updates for the specified reasons + /// + /// + /// The source. + /// The reasons. + /// + /// Must enter at least 1 reason + public static IObservable> WhereReasonsAreNot(this IObservable> source, + params ListChangeReason[] reasons) + { + if (reasons.Length == 0) + throw new ArgumentException("Must enter at least 1 reason", nameof(reasons)); + + var matches = new HashSet(reasons); + return source.Select(updates => + { + var filtered = updates.Where(u => !matches.Contains(u.Reason)).YieldWithoutIndex(); + return new ChangeSet(filtered); + }).NotEmpty(); + } + + #endregion + + #region Buffering + + /// + /// Buffers changes for an intial period only. After the period has elapsed, not further buffering occurs. + /// + /// The source changeset + /// The period to buffer, measure from the time that the first item arrives + /// The scheduler to buffer on + public static IObservable> BufferInitial(this IObservable> source, TimeSpan initalBuffer, IScheduler scheduler = null) + { + return source.DeferUntilLoaded().Publish(shared => + { + var initial = shared.Buffer(initalBuffer, scheduler ?? Scheduler.Default) + .FlattenBufferResult() + .Take(1); + + return initial.Concat(shared); + }); + } + + /// + /// Convert the result of a buffer operation to a change set + /// + /// + /// The source. + /// + public static IObservable> FlattenBufferResult(this IObservable>> source) + { + return source + .Where(x => x.Count != 0) + .Select(updates => new ChangeSet(updates.SelectMany(u => u))); + } + + /// + /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. + /// When a resume signal has been received the batched updates will be fired. + /// + /// The type of the object. + /// The source. + /// When true, observable begins to buffer and when false, window closes and buffered result if notified + /// The scheduler. + /// + /// source + public static IObservable> BufferIf([NotNull] this IObservable> source, + [NotNull] IObservable pauseIfTrueSelector, + IScheduler scheduler = null) + { + return BufferIf(source, pauseIfTrueSelector, false, scheduler); + } + + /// + /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. + /// When a resume signal has been received the batched updates will be fired. + /// + /// The type of the object. + /// The source. + /// When true, observable begins to buffer and when false, window closes and buffered result if notified + /// if set to true [intial pause state]. + /// The scheduler. + /// + /// source + public static IObservable> BufferIf([NotNull] this IObservable> source, + [NotNull] IObservable pauseIfTrueSelector, + bool intialPauseState = false, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (pauseIfTrueSelector == null) throw new ArgumentNullException(nameof(pauseIfTrueSelector)); + return BufferIf(source, pauseIfTrueSelector, intialPauseState, null, scheduler); + } + + /// + /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. + /// When a resume signal has been received the batched updates will be fired. + /// + /// The type of the object. + /// The source. + /// When true, observable begins to buffer and when false, window closes and buffered result if notified + /// Specify a time to ensure the buffer window does not stay open for too long + /// The scheduler. + /// + /// source + public static IObservable> BufferIf(this IObservable> source, + IObservable pauseIfTrueSelector, + TimeSpan? timeOut = null, + IScheduler scheduler = null) + { + return BufferIf(source, pauseIfTrueSelector, false, timeOut, scheduler); + } + + /// + /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received. + /// When a resume signal has been received the batched updates will be fired. + /// + /// The type of the object. + /// The source. + /// When true, observable begins to buffer and when false, window closes and buffered result if notified + /// if set to true [intial pause state]. + /// Specify a time to ensure the buffer window does not stay open for too long + /// The scheduler. + /// + /// source + public static IObservable> BufferIf(this IObservable> source, + IObservable pauseIfTrueSelector, + bool intialPauseState = false, + TimeSpan? timeOut = null, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (pauseIfTrueSelector == null) throw new ArgumentNullException(nameof(pauseIfTrueSelector)); + return new BufferIf(source, pauseIfTrueSelector, intialPauseState, timeOut, scheduler).Run(); + } + + /// + /// The latest copy of the cache is exposed for querying after each modification to the underlying data + /// + /// The type of the object. + /// The type of the destination. + /// The source. + /// The result selector. + /// + /// + /// source + /// or + /// resultSelector + /// + public static IObservable QueryWhenChanged( + this IObservable> source, + Func, TDestination> resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.QueryWhenChanged().Select(resultSelector); + } + + /// + /// The latest copy of the cache is exposed for querying i) after each modification to the underlying data ii) upon subscription + /// + /// The type of the object. + /// The source. + /// + /// source + public static IObservable> QueryWhenChanged([NotNull] this IObservable> source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return new QueryWhenChanged(source).Run(); + } + + /// + /// Converts the changeset into a fully formed collection. Each change in the source results in a new collection + /// + /// The type of the object. + /// The source. + /// + public static IObservable> ToCollection(this IObservable> source) + { + return source.QueryWhenChanged(items => items); + } + + /// + /// Converts the changeset into a fully formed sorted collection. Each change in the source results in a new sorted collection + /// + /// The type of the object. + /// The sort key + /// The source. + /// The sort function + /// The sort order. Defaults to ascending + /// + public static IObservable> ToSortedCollection(this IObservable> source, + Func sort, SortDirection sortOrder = SortDirection.Ascending) + { + return source.QueryWhenChanged(query => sortOrder == SortDirection.Ascending + ? new ReadOnlyCollectionLight(query.OrderBy(sort)) + : new ReadOnlyCollectionLight(query.OrderByDescending(sort))); + } + + /// + /// Converts the changeset into a fully formed sorted collection. Each change in the source results in a new sorted collection + /// + /// The type of the object. + /// The source. + /// The sort comparer + /// + public static IObservable> ToSortedCollection(this IObservable> source, + IComparer comparer) + { + return source.QueryWhenChanged(query => + { + var items = query.AsList(); + items.Sort(comparer); + return new ReadOnlyCollectionLight(items); + }); + } + + + /// + /// Defer the subscribtion until loaded and skip initial changeset + /// + /// The type of the object. + /// The source. + /// + /// source + public static IObservable> SkipInitial(this IObservable> source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.DeferUntilLoaded().Skip(1); + } + + /// + /// Defer the subscription until the stream has been inflated with data + /// + /// The type of the object. + /// The source. + /// + public static IObservable> DeferUntilLoaded([NotNull] this IObservable> source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return new DeferUntilLoaded(source).Run(); + } + + /// + /// Defer the subscription until the cache has been inflated with data + /// + /// The type of the object. + /// The source. + /// + public static IObservable> DeferUntilLoaded(this IObservableList source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Connect().DeferUntilLoaded(); + } + + #endregion + + #region Virtualisation / Paging + + /// + /// Virtualises the source using parameters provided via the requests observable + /// + /// + /// The source. + /// The requests. + /// + public static IObservable> Virtualise([NotNull] this IObservable> source, + [NotNull] IObservable requests) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (requests == null) throw new ArgumentNullException(nameof(requests)); + return new Virtualiser(source, requests).Run(); + } + + /// + /// Limits the size of the result set to the specified number of items + /// + /// + /// The source. + /// The number of items. + /// + public static IObservable> Top([NotNull] this IObservable> source, + int numberOfItems) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (numberOfItems <= 0) + throw new ArgumentOutOfRangeException(nameof(numberOfItems), + "Number of items should be greater than zero"); + + return source.Virtualise(Observable.Return(new VirtualRequest(0, numberOfItems))); + } + + + /// + /// Applies paging to the the data source + /// + /// + /// The source. + /// Observable to control page requests + /// + public static IObservable> Page([NotNull] this IObservable> source, + [NotNull] IObservable requests) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (requests == null) throw new ArgumentNullException(nameof(requests)); + return new Pager(source, requests).Run(); + } + + #endregion + + #region Expiry / size limiter + + /// + /// Limits the size of the source cache to the specified limit. + /// Notifies which items have been removed from the source list. + /// + /// + /// The source. + /// The size limit. + /// The scheduler. + /// + /// sizeLimit cannot be zero + /// source + /// sizeLimit cannot be zero + public static IObservable> LimitSizeTo([NotNull] this ISourceList source, int sizeLimit, + IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (sizeLimit <= 0) throw new ArgumentException("sizeLimit cannot be zero", nameof(sizeLimit)); + + var locker = new object(); + var limiter = new LimitSizeTo(source, sizeLimit, scheduler ?? Scheduler.Default, locker); + + return limiter.Run().Synchronize(locker).Do(source.RemoveMany); + } + + /// + /// Removes items from the cache according to the value specified by the time selector function + /// + /// + /// The source. + /// Selector returning when to expire the item. Return null for non-expiring item + /// The scheduler + /// + /// + /// + public static IObservable> ExpireAfter([NotNull] this ISourceList source, + [NotNull] Func timeSelector, IScheduler scheduler = null) + { + return source.ExpireAfter(timeSelector, null, scheduler); + } + + /// + /// Removes items from the cache according to the value specified by the time selector function + /// + /// + /// The source. + /// Selector returning when to expire the item. Return null for non-expiring item + /// Enter the polling interval to optimise expiry timers, if ommited 1 timer is created for each unique expiry time + /// The scheduler + /// + /// + /// + public static IObservable> ExpireAfter([NotNull] this ISourceList source, + [NotNull] Func timeSelector, TimeSpan? pollingInterval = null, IScheduler scheduler = null) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (timeSelector == null) throw new ArgumentNullException(nameof(timeSelector)); + + var locker = new object(); + var limiter = new ExpireAfter(source, timeSelector, pollingInterval, scheduler ?? Scheduler.Default, + locker); + + return limiter.Run().Synchronize(locker).Do(source.RemoveMany); + } + + #endregion + + #region Logical collection operators + + /// + /// Apply a logical Or operator between the collections. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> Or([NotNull] this ICollection>> sources) + { + return sources.Combine(CombineOperator.Or); + } + + /// + /// Apply a logical Or operator between the collections. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// The others. + /// + public static IObservable> Or([NotNull] this IObservable> source, + params IObservable>[] others) + { + return source.Combine(CombineOperator.Or, others); + } + + /// + /// Dynamically apply a logical Or operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> Or( + [NotNull] this IObservableList>> sources) + { + return sources.Combine(CombineOperator.Or); + } + + /// + /// Dynamically apply a logical Or operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> Or([NotNull] this IObservableList> sources) + { + return sources.Combine(CombineOperator.Or); + } + + /// + /// Dynamically apply a logical Or operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> Or([NotNull] this IObservableList> sources) + { + return sources.Combine(CombineOperator.Or); + } + + /// + /// Apply a logical Xor operator between the collections. + /// Items which are only in one of the sources are included in the result + /// + /// + /// The source. + /// The others. + /// + public static IObservable> Xor([NotNull] this IObservable> source, + params IObservable>[] others) + { + return source.Combine(CombineOperator.Xor, others); + } + + /// + /// Apply a logical Xor operator between the collections. + /// Items which are only in one of the sources are included in the result + /// + /// + /// The sources. + /// > + public static IObservable> Xor([NotNull] this ICollection>> sources) + { + return sources.Combine(CombineOperator.Xor); + } + + /// + /// Dynamically apply a logical Xor operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> Xor( + [NotNull] this IObservableList>> sources) + { + return sources.Combine(CombineOperator.Xor); + } + + /// + /// Dynamically apply a logical Xor operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> Xor([NotNull] this IObservableList> sources) + { + return sources.Combine(CombineOperator.Xor); + } + + /// + /// Dynamically apply a logical Xor operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> Xor([NotNull] this IObservableList> sources) + { + return sources.Combine(CombineOperator.Xor); + } + + /// + /// Apply a logical And operator between the collections. + /// Items which are in all of the sources are included in the result + /// + /// + /// The source. + /// The others. + /// + public static IObservable> And([NotNull] this IObservable> source, + params IObservable>[] others) + { + return source.Combine(CombineOperator.And, others); + } + + /// + /// Apply a logical And operator between the collections. + /// Items which are in all of the sources are included in the result + /// + /// + /// The sources. + /// > + public static IObservable> And([NotNull] this ICollection>> sources) + { + return sources.Combine(CombineOperator.And); + } + + /// + /// Dynamically apply a logical And operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> And( + [NotNull] this IObservableList>> sources) + { + return sources.Combine(CombineOperator.And); + } + + /// + /// Dynamically apply a logical And operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> And([NotNull] this IObservableList> sources) + { + return sources.Combine(CombineOperator.And); + } + + /// + /// Dynamically apply a logical And operator between the items in the outer observable list. + /// Items which are in any of the sources are included in the result + /// + /// + /// The source. + /// + public static IObservable> And([NotNull] this IObservableList> sources) + { + return sources.Combine(CombineOperator.And); + } + + /// + /// Apply a logical Except operator between the collections. + /// Items which are in the source and not in the others are included in the result + /// + /// + /// The source. + /// The others. + /// + public static IObservable> Except([NotNull] this IObservable> source, + params IObservable>[] others) + { + return source.Combine(CombineOperator.Except, others); + } + + /// + /// Apply a logical Except operator between the collections. + /// Items which are in the source and not in the others are included in the result + /// + /// + /// The sources. + /// > + public static IObservable> Except( + [NotNull] this ICollection>> sources) + { + return sources.Combine(CombineOperator.Except); + } + + /// + /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. + /// + /// + /// The source. + /// + public static IObservable> Except( + [NotNull] this IObservableList>> sources) + { + return sources.Combine(CombineOperator.Except); + } + + /// + /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. + /// + /// + /// The source. + /// + public static IObservable> Except([NotNull] this IObservableList> sources) + { + return sources.Combine(CombineOperator.Except); + } + + /// + /// Dynamically apply a logical Except operator. Items from the first observable list are included when an equivalent item does not exist in the other sources. + /// + /// + /// The source. + /// + public static IObservable> Except([NotNull] this IObservableList> sources) + { + return sources.Combine(CombineOperator.Except); + } + + private static IObservable> Combine( + [NotNull] this ICollection>> sources, + CombineOperator type) + { + if (sources == null) throw new ArgumentNullException(nameof(sources)); + + return new Combiner(sources, type).Run(); + } + + private static IObservable> Combine([NotNull] this IObservable> source, + CombineOperator type, + params IObservable>[] others) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (others.Length == 0) + throw new ArgumentException("Must be at least one item to combine with", nameof(others)); + + var items = source.EnumerateOne().Union(others).ToList(); + return new Combiner(items, type).Run(); + } + + private static IObservable> Combine([NotNull] this IObservableList> sources, + CombineOperator type) + { + if (sources == null) throw new ArgumentNullException(nameof(sources)); + + return Observable.Create>(observer => + { + var changesSetList = sources.Connect().Transform(s => s.Connect()).AsObservableList(); + var subscriber = changesSetList.Combine(type).SubscribeSafe(observer); + return new CompositeDisposable(changesSetList, subscriber); + }); + } + + private static IObservable> Combine([NotNull] this IObservableList> sources, + CombineOperator type) + { + if (sources == null) throw new ArgumentNullException(nameof(sources)); + + return Observable.Create>(observer => + { + var changesSetList = sources.Connect().Transform(s => s.Connect()).AsObservableList(); + var subscriber = changesSetList.Combine(type).SubscribeSafe(observer); + return new CompositeDisposable(changesSetList, subscriber); + }); + } + + private static IObservable> Combine( + [NotNull] this IObservableList>> sources, CombineOperator type) + { + if (sources == null) throw new ArgumentNullException(nameof(sources)); + return new DynamicCombiner(sources, type).Run(); + } + + #endregion + + #region Switch + + /// + /// Transforms an observable sequence of observable lists into a single sequence + /// producing values only from the most recent observable sequence. + /// Each time a new inner observable sequence is received, unsubscribe from the + /// previous inner observable sequence and clear the existing result set + /// + /// The type of the object. + /// The source. + /// + /// The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. + /// + /// + /// is null. + public static IObservable> Switch(this IObservable> sources) + { + if (sources == null) throw new ArgumentNullException(nameof(sources)); + return sources.Select(cache => cache.Connect()).Switch(); + } + + /// + /// Transforms an observable sequence of observable changes sets into an observable sequence + /// producing values only from the most recent observable sequence. + /// Each time a new inner observable sequence is received, unsubscribe from the + /// previous inner observable sequence and clear the existing resukt set + /// + /// The type of the object. + /// The source. + /// + /// The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received. + /// + /// + /// is null. + public static IObservable> Switch(this IObservable>> sources) + { + if (sources == null) throw new ArgumentNullException(nameof(sources)); + return new Switch(sources).Run(); + } + + #endregion + + + #region Start with + + /// + /// Prepends an empty changeset to the source + /// + public static IObservable> StartWithEmpty(this IObservable> source) + { + return source.StartWith(ChangeSet.Empty); + } + + + + #endregion + } +} diff --git a/DynamicData/List/PageChangeSet.cs b/src/DynamicData/List/PageChangeSet.cs similarity index 100% rename from DynamicData/List/PageChangeSet.cs rename to src/DynamicData/List/PageChangeSet.cs diff --git a/DynamicData/List/RangeChange.cs b/src/DynamicData/List/RangeChange.cs similarity index 100% rename from DynamicData/List/RangeChange.cs rename to src/DynamicData/List/RangeChange.cs diff --git a/DynamicData/List/SortException.cs b/src/DynamicData/List/SortException.cs similarity index 100% rename from DynamicData/List/SortException.cs rename to src/DynamicData/List/SortException.cs diff --git a/DynamicData/List/SortOptions.cs b/src/DynamicData/List/SortOptions.cs similarity index 100% rename from DynamicData/List/SortOptions.cs rename to src/DynamicData/List/SortOptions.cs diff --git a/DynamicData/List/SourceList.cs b/src/DynamicData/List/SourceList.cs similarity index 96% rename from DynamicData/List/SourceList.cs rename to src/DynamicData/List/SourceList.cs index 5d5563b26..a7f6c0c54 100644 --- a/DynamicData/List/SourceList.cs +++ b/src/DynamicData/List/SourceList.cs @@ -1,180 +1,180 @@ -using System; -using System.Collections.Generic; -using System.Reactive.Disposables; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using DynamicData.Annotations; -using DynamicData.List.Internal; - -// ReSharper disable once CheckNamespace -namespace DynamicData -{ - /// - /// An editable observable list - /// - /// The type of the object. - public sealed class SourceList : ISourceList - { - private readonly ISubject> _changes = new Subject>(); - private readonly Subject> _changesPreview = new Subject>(); - private readonly Lazy> _countChanged = new Lazy>(() => new Subject()); - private readonly ReaderWriter _readerWriter = new ReaderWriter(); - private readonly IDisposable _cleanUp; - private readonly object _locker = new object(); - private readonly object _writeLock = new object(); - - private int _editLevel; - - /// - /// Initializes a new instance of the class. - /// - /// The source. - public SourceList(IObservable> source = null) - { - var loader = source == null ? Disposable.Empty : LoadFromSource(source); - - _cleanUp = Disposable.Create(() => - { - loader.Dispose(); - OnCompleted(); - if (_countChanged.IsValueCreated) - { - _countChanged.Value.OnCompleted(); - } - }); - } - - private IDisposable LoadFromSource(IObservable> source) - { - return source - .Finally(OnCompleted) - .Select(_readerWriter.Write) - .Subscribe(InvokeNext, OnError, OnCompleted); - } - - /// - public void Edit([NotNull] Action> updateAction) - { - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); - - lock (_writeLock) - { - IChangeSet changes = null; - - _editLevel++; - - if (_editLevel == 1) - { - if (_changesPreview.HasObservers) - { - changes = _readerWriter.WriteWithPreview(updateAction, InvokeNextPreview); - } - else - { - changes = _readerWriter.Write(updateAction); - } - } - else - { - _readerWriter.WriteNested(updateAction); - } - - _editLevel--; - - if (_editLevel == 0) - { - InvokeNext(changes); - } - } - } - - private void InvokeNextPreview(IChangeSet changes) - { - if (changes.Count == 0) return; - - lock (_locker) - { - _changesPreview.OnNext(changes); - } - } - - private void InvokeNext(IChangeSet changes) - { - if (changes.Count == 0) return; - - lock (_locker) - { - _changes.OnNext(changes); - - if (_countChanged.IsValueCreated) - _countChanged.Value.OnNext(_readerWriter.Count); - } - } - - - private void OnCompleted() - { - lock (_locker) - { - _changesPreview.OnCompleted(); - _changes.OnCompleted(); - } - } - - private void OnError(Exception exception) - { - lock (_locker) - { - _changesPreview.OnError(exception); - _changes.OnError(exception); - } - } - - /// - public IEnumerable Items => _readerWriter.Items; - - /// - public int Count => _readerWriter.Count; - - /// - public IObservable CountChanged => _countChanged.Value.StartWith(_readerWriter.Count).DistinctUntilChanged(); - - /// - public IObservable> Connect(Func predicate = null) - { - var observable = Observable.Create>(observer => - { - lock (_locker) - { - var initial = new ChangeSet(new[] {new Change(ListChangeReason.AddRange, _readerWriter.Items)}); - if (initial.TotalChanges > 0) observer.OnNext(initial); - var source = _changes.Finally(observer.OnCompleted); - - return source.SubscribeSafe(observer); - } - }); - - if (predicate != null) - observable = new FilterStatic(observable, predicate).Run(); - - return observable; - } - - /// - public IObservable> Preview(Func predicate = null) - { - IObservable> observable = _changesPreview; - - if (predicate != null) - observable = new FilterStatic(observable, predicate).Run(); - - return observable; - } - - /// - public void Dispose() - { - _cleanUp.Dispose(); - } - } -} +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using DynamicData.Annotations; +using DynamicData.List.Internal; + +// ReSharper disable once CheckNamespace +namespace DynamicData +{ + /// + /// An editable observable list + /// + /// The type of the object. + public sealed class SourceList : ISourceList + { + private readonly ISubject> _changes = new Subject>(); + private readonly Subject> _changesPreview = new Subject>(); + private readonly Lazy> _countChanged = new Lazy>(() => new Subject()); + private readonly ReaderWriter _readerWriter = new ReaderWriter(); + private readonly IDisposable _cleanUp; + private readonly object _locker = new object(); + private readonly object _writeLock = new object(); + + private int _editLevel; + + /// + /// Initializes a new instance of the class. + /// + /// The source. + public SourceList(IObservable> source = null) + { + var loader = source == null ? Disposable.Empty : LoadFromSource(source); + + _cleanUp = Disposable.Create(() => + { + loader.Dispose(); + OnCompleted(); + if (_countChanged.IsValueCreated) + { + _countChanged.Value.OnCompleted(); + } + }); + } + + private IDisposable LoadFromSource(IObservable> source) + { + return source + .Finally(OnCompleted) + .Select(_readerWriter.Write) + .Subscribe(InvokeNext, OnError, OnCompleted); + } + + /// + public void Edit([NotNull] Action> updateAction) + { + if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); + + lock (_writeLock) + { + IChangeSet changes = null; + + _editLevel++; + + if (_editLevel == 1) + { + if (_changesPreview.HasObservers) + { + changes = _readerWriter.WriteWithPreview(updateAction, InvokeNextPreview); + } + else + { + changes = _readerWriter.Write(updateAction); + } + } + else + { + _readerWriter.WriteNested(updateAction); + } + + _editLevel--; + + if (_editLevel == 0) + { + InvokeNext(changes); + } + } + } + + private void InvokeNextPreview(IChangeSet changes) + { + if (changes.Count == 0) return; + + lock (_locker) + { + _changesPreview.OnNext(changes); + } + } + + private void InvokeNext(IChangeSet changes) + { + if (changes.Count == 0) return; + + lock (_locker) + { + _changes.OnNext(changes); + + if (_countChanged.IsValueCreated) + _countChanged.Value.OnNext(_readerWriter.Count); + } + } + + + private void OnCompleted() + { + lock (_locker) + { + _changesPreview.OnCompleted(); + _changes.OnCompleted(); + } + } + + private void OnError(Exception exception) + { + lock (_locker) + { + _changesPreview.OnError(exception); + _changes.OnError(exception); + } + } + + /// + public IEnumerable Items => _readerWriter.Items; + + /// + public int Count => _readerWriter.Count; + + /// + public IObservable CountChanged => _countChanged.Value.StartWith(_readerWriter.Count).DistinctUntilChanged(); + + /// + public IObservable> Connect(Func predicate = null) + { + var observable = Observable.Create>(observer => + { + lock (_locker) + { + var initial = new ChangeSet(new[] {new Change(ListChangeReason.AddRange, _readerWriter.Items)}); + if (initial.TotalChanges > 0) observer.OnNext(initial); + var source = _changes.Finally(observer.OnCompleted); + + return source.SubscribeSafe(observer); + } + }); + + if (predicate != null) + observable = new FilterStatic(observable, predicate).Run(); + + return observable; + } + + /// + public IObservable> Preview(Func predicate = null) + { + IObservable> observable = _changesPreview; + + if (predicate != null) + observable = new FilterStatic(observable, predicate).Run(); + + return observable; + } + + /// + public void Dispose() + { + _cleanUp.Dispose(); + } + } +} diff --git a/DynamicData/List/SourceListEditConvenienceEx.cs b/src/DynamicData/List/SourceListEditConvenienceEx.cs similarity index 100% rename from DynamicData/List/SourceListEditConvenienceEx.cs rename to src/DynamicData/List/SourceListEditConvenienceEx.cs diff --git a/DynamicData/List/SourceListEx.cs b/src/DynamicData/List/SourceListEx.cs similarity index 100% rename from DynamicData/List/SourceListEx.cs rename to src/DynamicData/List/SourceListEx.cs diff --git a/DynamicData/List/Tests/ChangeSetAggregator.cs b/src/DynamicData/List/Tests/ChangeSetAggregator.cs similarity index 100% rename from DynamicData/List/Tests/ChangeSetAggregator.cs rename to src/DynamicData/List/Tests/ChangeSetAggregator.cs diff --git a/DynamicData/List/Tests/TestEx.cs b/src/DynamicData/List/Tests/TestEx.cs similarity index 100% rename from DynamicData/List/Tests/TestEx.cs rename to src/DynamicData/List/Tests/TestEx.cs diff --git a/DynamicData/List/UnspecifiedIndexException.cs b/src/DynamicData/List/UnspecifiedIndexException.cs similarity index 100% rename from DynamicData/List/UnspecifiedIndexException.cs rename to src/DynamicData/List/UnspecifiedIndexException.cs diff --git a/DynamicData/List/VirtualChangeSet.cs b/src/DynamicData/List/VirtualChangeSet.cs similarity index 100% rename from DynamicData/List/VirtualChangeSet.cs rename to src/DynamicData/List/VirtualChangeSet.cs diff --git a/DynamicData/ObservableChangeSet.cs b/src/DynamicData/ObservableChangeSet.cs similarity index 98% rename from DynamicData/ObservableChangeSet.cs rename to src/DynamicData/ObservableChangeSet.cs index 60babd365..56b4dd3d9 100644 --- a/DynamicData/ObservableChangeSet.cs +++ b/src/DynamicData/ObservableChangeSet.cs @@ -1,438 +1,438 @@ -using System; -using System.Reactive.Disposables; -using System.Reactive.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace DynamicData -{ - /// - /// Creation methods for observable change sets - /// - public static class ObservableChangeSet - { - - #region Cache Create Methods - - /// - /// Creates an observable cache from a specified Subscribe method implementation. - /// - /// The type of the elements contained in the observable cache - /// The type of the specified key - /// Implementation of the resulting observable cache's Subscribe method. - /// The key selector. - /// The observable cache with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, Action> subscribe, Func keySelector) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - return Create(cache => - { - var action = subscribe(cache); - return Disposable.Create(() => { action?.Invoke(); }); - },keySelector); - } - - /// - /// Creates an observable cache from a specified Subscribe method implementation. - /// - /// The type of the elements contained in the observable cache - /// The type of the specified key - /// Implementation of the resulting observable cache's Subscribe method. - /// The key selector. - /// The observable cache with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, IDisposable> subscribe, Func keySelector) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return Observable.Create>(observer => - { - var cache = new SourceCache(keySelector); - var disposable = new SingleAssignmentDisposable(); - - try - { - disposable.Disposable = subscribe(cache); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(disposable, - Disposable.Create(observer.OnCompleted), - cache.Connect().SubscribeSafe(observer), - cache); - }); - } - - /// - /// Creates an observable cache from a specified Subscribe method implementation. - /// - /// The type of the elements contained in the observable cache - /// The type of the specified key - /// Implementation of the resulting observable cache's Subscribe method. - /// The key selector. - /// The observable cache with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, Task> subscribe, Func keySelector) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return Create(async (list, ct) => await subscribe(list), keySelector); - } - - /// - /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. - /// - /// The type of the elements contained in the observable cache - /// The type of the specified key - /// Implementation of the resulting observable cache's Subscribe method. - /// The key selector. - /// The observable cache with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return Observable.Create>(async (observer, ct) => - { - var cache = new SourceCache(keySelector); - var disposable = new SingleAssignmentDisposable(); - - try - { - disposable.Disposable = await subscribe(cache, ct); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), - cache, - disposable, - Disposable.Create(observer.OnCompleted)); - }); - } - - /// - /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. - /// - /// The type of the elements contained in the observable cache - /// The type of the specified key - /// Implementation of the resulting observable cache's Subscribe method. - /// The key selector. - /// The observable cache with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, Task> subscribe, Func keySelector) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return Create(async (list, ct) => await subscribe(list),keySelector); - } - - - /// - /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. - /// - /// The type of the elements contained in the observable cache - /// The type of the specified key - /// Implementation of the resulting observable cache's Subscribe method. - /// The key selector. - /// The observable cache with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return Observable.Create>(async (observer, ct) => - { - var cache = new SourceCache(keySelector); - Action disposeAction = null; - - try - { - disposeAction = await subscribe(cache, ct); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), - cache, Disposable.Create(() => - { - observer.OnCompleted(); - disposeAction?.Invoke(); - })); - }); - } - - /// - /// Creates an observable cache from a specified asynchronous Subscribe method. - /// - /// The type of the elements contained in the observable cache - /// The type of the specified key - /// Implementation of the resulting observable cache's Subscribe method. - /// The key selector. - /// The observable cache with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, Task> subscribe, Func keySelector) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return Observable.Create>(async observer => - { - var cache = new SourceCache(keySelector); - - try - { - await subscribe(cache); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), - cache, - Disposable.Create(observer.OnCompleted)); - }); - } - - /// - /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. - /// - /// The type of the elements contained in the observable cache - /// The type of the specified key - /// Implementation of the resulting observable cache's Subscribe method. - /// The key selector. - /// The observable cache with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return Observable.Create>(async (observer, ct) => - { - var cache = new SourceCache(keySelector); - - try - { - await subscribe(cache, ct); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), - cache, - Disposable.Create(observer.OnCompleted)); - }); - } - - - #endregion - - #region List Create Methods - - /// - /// Creates an observable list from a specified Subscribe method implementation. - /// - /// The type of the elements contained in the observable list - /// Implementation of the resulting observable list's Subscribe method. - /// The observable list with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, Action> subscribe) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - - return Create(list => - { - var action = subscribe(list); - return Disposable.Create(() => { action?.Invoke(); }); - }); - } - - /// - /// Creates an observable list from a specified Subscribe method implementation. - /// - /// The type of the elements contained in the observable list - /// Implementation of the resulting observable list's Subscribe method. - /// The observable list with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, IDisposable> subscribe) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - - return Observable.Create>(observer => - { - var list = new SourceList(); - IDisposable disposeAction = null; - - try - { - disposeAction = subscribe(list); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, Disposable.Create(() => - { - observer.OnCompleted(); - disposeAction?.Dispose(); - })); - }); - } - - /// - /// Creates an observable list from a specified Subscribe method implementation. - /// - /// The type of the elements contained in the observable list - /// Implementation of the resulting observable list's Subscribe method. - /// The observable list with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, Task> subscribe) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - return Create(async (list, ct) => await subscribe(list)); - } - - /// - /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. - /// - /// The type of the elements contained in the observable list - /// Implementation of the resulting observable list's Subscribe method. - /// The observable list with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, CancellationToken, Task> subscribe) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - - return Observable.Create>(async (observer, ct) => - { - var list = new SourceList(); - IDisposable disposeAction = null; - SingleAssignmentDisposable actionDisposable = new SingleAssignmentDisposable(); - - try - { - disposeAction = await subscribe(list, ct); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, actionDisposable, Disposable.Create(() => - { - observer.OnCompleted(); - disposeAction?.Dispose(); - })); - }); - } - - /// - /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. - /// - /// The type of the elements contained in the observable list - /// Implementation of the resulting observable list's Subscribe method. - /// The observable list with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, Task> subscribe) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - return Create(async (list, ct) => await subscribe(list)); - } - - - /// - /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. - /// - /// The type of the elements contained in the observable list - /// Implementation of the resulting observable list's Subscribe method. - /// The observable list with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, CancellationToken, Task> subscribe) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - - return Observable.Create>(async (observer, ct) => - { - var list = new SourceList(); - Action disposeAction = null; - - try - { - disposeAction = await subscribe(list, ct); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, Disposable.Create(() => - { - observer.OnCompleted(); - disposeAction?.Invoke(); - })); - }); - } - - /// - /// Creates an observable list from a specified asynchronous Subscribe method. - /// - /// The type of the elements contained in the observable list - /// Implementation of the resulting observable list's Subscribe method. - /// The observable list with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, Task> subscribe) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - - return Observable.Create>(async observer => - { - var list = new SourceList(); - - try - { - await subscribe(list); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, Disposable.Create(observer.OnCompleted)); - }); - } - - /// - /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. - /// - /// The type of the elements contained in the observable list - /// Implementation of the resulting observable list's Subscribe method. - /// The observable list with the specified implementation for the Subscribe method. - public static IObservable> Create(Func, CancellationToken, Task> subscribe) - { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - - return Observable.Create>(async (observer, ct) => - { - var list = new SourceList(); - - try - { - await subscribe(list,ct); - } - catch (Exception e) - { - observer.OnError(e); - } - - return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, Disposable.Create(observer.OnCompleted)); - }); - } - - - #endregion - } +using System; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace DynamicData +{ + /// + /// Creation methods for observable change sets + /// + public static class ObservableChangeSet + { + + #region Cache Create Methods + + /// + /// Creates an observable cache from a specified Subscribe method implementation. + /// + /// The type of the elements contained in the observable cache + /// The type of the specified key + /// Implementation of the resulting observable cache's Subscribe method. + /// The key selector. + /// The observable cache with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, Action> subscribe, Func keySelector) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + return Create(cache => + { + var action = subscribe(cache); + return Disposable.Create(() => { action?.Invoke(); }); + },keySelector); + } + + /// + /// Creates an observable cache from a specified Subscribe method implementation. + /// + /// The type of the elements contained in the observable cache + /// The type of the specified key + /// Implementation of the resulting observable cache's Subscribe method. + /// The key selector. + /// The observable cache with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, IDisposable> subscribe, Func keySelector) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + + return Observable.Create>(observer => + { + var cache = new SourceCache(keySelector); + var disposable = new SingleAssignmentDisposable(); + + try + { + disposable.Disposable = subscribe(cache); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(disposable, + Disposable.Create(observer.OnCompleted), + cache.Connect().SubscribeSafe(observer), + cache); + }); + } + + /// + /// Creates an observable cache from a specified Subscribe method implementation. + /// + /// The type of the elements contained in the observable cache + /// The type of the specified key + /// Implementation of the resulting observable cache's Subscribe method. + /// The key selector. + /// The observable cache with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, Task> subscribe, Func keySelector) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + + return Create(async (list, ct) => await subscribe(list), keySelector); + } + + /// + /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. + /// + /// The type of the elements contained in the observable cache + /// The type of the specified key + /// Implementation of the resulting observable cache's Subscribe method. + /// The key selector. + /// The observable cache with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + + return Observable.Create>(async (observer, ct) => + { + var cache = new SourceCache(keySelector); + var disposable = new SingleAssignmentDisposable(); + + try + { + disposable.Disposable = await subscribe(cache, ct); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), + cache, + disposable, + Disposable.Create(observer.OnCompleted)); + }); + } + + /// + /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. + /// + /// The type of the elements contained in the observable cache + /// The type of the specified key + /// Implementation of the resulting observable cache's Subscribe method. + /// The key selector. + /// The observable cache with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, Task> subscribe, Func keySelector) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + + return Create(async (list, ct) => await subscribe(list),keySelector); + } + + + /// + /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. + /// + /// The type of the elements contained in the observable cache + /// The type of the specified key + /// Implementation of the resulting observable cache's Subscribe method. + /// The key selector. + /// The observable cache with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + + return Observable.Create>(async (observer, ct) => + { + var cache = new SourceCache(keySelector); + Action disposeAction = null; + + try + { + disposeAction = await subscribe(cache, ct); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), + cache, Disposable.Create(() => + { + observer.OnCompleted(); + disposeAction?.Invoke(); + })); + }); + } + + /// + /// Creates an observable cache from a specified asynchronous Subscribe method. + /// + /// The type of the elements contained in the observable cache + /// The type of the specified key + /// Implementation of the resulting observable cache's Subscribe method. + /// The key selector. + /// The observable cache with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, Task> subscribe, Func keySelector) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + + return Observable.Create>(async observer => + { + var cache = new SourceCache(keySelector); + + try + { + await subscribe(cache); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), + cache, + Disposable.Create(observer.OnCompleted)); + }); + } + + /// + /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. + /// + /// The type of the elements contained in the observable cache + /// The type of the specified key + /// Implementation of the resulting observable cache's Subscribe method. + /// The key selector. + /// The observable cache with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + + return Observable.Create>(async (observer, ct) => + { + var cache = new SourceCache(keySelector); + + try + { + await subscribe(cache, ct); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), + cache, + Disposable.Create(observer.OnCompleted)); + }); + } + + + #endregion + + #region List Create Methods + + /// + /// Creates an observable list from a specified Subscribe method implementation. + /// + /// The type of the elements contained in the observable list + /// Implementation of the resulting observable list's Subscribe method. + /// The observable list with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, Action> subscribe) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + + return Create(list => + { + var action = subscribe(list); + return Disposable.Create(() => { action?.Invoke(); }); + }); + } + + /// + /// Creates an observable list from a specified Subscribe method implementation. + /// + /// The type of the elements contained in the observable list + /// Implementation of the resulting observable list's Subscribe method. + /// The observable list with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, IDisposable> subscribe) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + + return Observable.Create>(observer => + { + var list = new SourceList(); + IDisposable disposeAction = null; + + try + { + disposeAction = subscribe(list); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, Disposable.Create(() => + { + observer.OnCompleted(); + disposeAction?.Dispose(); + })); + }); + } + + /// + /// Creates an observable list from a specified Subscribe method implementation. + /// + /// The type of the elements contained in the observable list + /// Implementation of the resulting observable list's Subscribe method. + /// The observable list with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, Task> subscribe) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + return Create(async (list, ct) => await subscribe(list)); + } + + /// + /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. + /// + /// The type of the elements contained in the observable list + /// Implementation of the resulting observable list's Subscribe method. + /// The observable list with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, CancellationToken, Task> subscribe) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + + return Observable.Create>(async (observer, ct) => + { + var list = new SourceList(); + IDisposable disposeAction = null; + SingleAssignmentDisposable actionDisposable = new SingleAssignmentDisposable(); + + try + { + disposeAction = await subscribe(list, ct); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, actionDisposable, Disposable.Create(() => + { + observer.OnCompleted(); + disposeAction?.Dispose(); + })); + }); + } + + /// + /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. + /// + /// The type of the elements contained in the observable list + /// Implementation of the resulting observable list's Subscribe method. + /// The observable list with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, Task> subscribe) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + return Create(async (list, ct) => await subscribe(list)); + } + + + /// + /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. + /// + /// The type of the elements contained in the observable list + /// Implementation of the resulting observable list's Subscribe method. + /// The observable list with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, CancellationToken, Task> subscribe) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + + return Observable.Create>(async (observer, ct) => + { + var list = new SourceList(); + Action disposeAction = null; + + try + { + disposeAction = await subscribe(list, ct); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, Disposable.Create(() => + { + observer.OnCompleted(); + disposeAction?.Invoke(); + })); + }); + } + + /// + /// Creates an observable list from a specified asynchronous Subscribe method. + /// + /// The type of the elements contained in the observable list + /// Implementation of the resulting observable list's Subscribe method. + /// The observable list with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, Task> subscribe) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + + return Observable.Create>(async observer => + { + var list = new SourceList(); + + try + { + await subscribe(list); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, Disposable.Create(observer.OnCompleted)); + }); + } + + /// + /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. + /// + /// The type of the elements contained in the observable list + /// Implementation of the resulting observable list's Subscribe method. + /// The observable list with the specified implementation for the Subscribe method. + public static IObservable> Create(Func, CancellationToken, Task> subscribe) + { + if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + + return Observable.Create>(async (observer, ct) => + { + var list = new SourceList(); + + try + { + await subscribe(list,ct); + } + catch (Exception e) + { + observer.OnError(e); + } + + return new CompositeDisposable(list.Connect().SubscribeSafe(observer), list, Disposable.Create(observer.OnCompleted)); + }); + } + + + #endregion + } } \ No newline at end of file diff --git a/DynamicData/ObsoleteEx.cs b/src/DynamicData/ObsoleteEx.cs similarity index 100% rename from DynamicData/ObsoleteEx.cs rename to src/DynamicData/ObsoleteEx.cs diff --git a/DynamicData/Platforms/net45/PLinqFilteredUpdater.cs b/src/DynamicData/Platforms/net45/PLinqFilteredUpdater.cs similarity index 100% rename from DynamicData/Platforms/net45/PLinqFilteredUpdater.cs rename to src/DynamicData/Platforms/net45/PLinqFilteredUpdater.cs diff --git a/DynamicData/Platforms/net45/PSubscribeMany.cs b/src/DynamicData/Platforms/net45/PSubscribeMany.cs similarity index 100% rename from DynamicData/Platforms/net45/PSubscribeMany.cs rename to src/DynamicData/Platforms/net45/PSubscribeMany.cs diff --git a/DynamicData/Platforms/net45/PTransform.cs b/src/DynamicData/Platforms/net45/PTransform.cs similarity index 100% rename from DynamicData/Platforms/net45/PTransform.cs rename to src/DynamicData/Platforms/net45/PTransform.cs diff --git a/DynamicData/Platforms/net45/ParallelEx.cs b/src/DynamicData/Platforms/net45/ParallelEx.cs similarity index 100% rename from DynamicData/Platforms/net45/ParallelEx.cs rename to src/DynamicData/Platforms/net45/ParallelEx.cs diff --git a/DynamicData/Platforms/net45/ParallelOperators.cs b/src/DynamicData/Platforms/net45/ParallelOperators.cs similarity index 100% rename from DynamicData/Platforms/net45/ParallelOperators.cs rename to src/DynamicData/Platforms/net45/ParallelOperators.cs diff --git a/DynamicData/Platforms/net45/ParallelType.cs b/src/DynamicData/Platforms/net45/ParallelType.cs similarity index 100% rename from DynamicData/Platforms/net45/ParallelType.cs rename to src/DynamicData/Platforms/net45/ParallelType.cs diff --git a/DynamicData/Platforms/net45/ParallelisationOptions.cs b/src/DynamicData/Platforms/net45/ParallelisationOptions.cs similarity index 100% rename from DynamicData/Platforms/net45/ParallelisationOptions.cs rename to src/DynamicData/Platforms/net45/ParallelisationOptions.cs diff --git a/DynamicData/Properties/Annotations.cs b/src/DynamicData/Properties/Annotations.cs similarity index 100% rename from DynamicData/Properties/Annotations.cs rename to src/DynamicData/Properties/Annotations.cs diff --git a/global.json b/src/global.json similarity index 100% rename from global.json rename to src/global.json