Dynamic Data

repository·main·Indexed 23 days ago

https://github.com/reactivemarbles/dynamicdata

A portable class library that brings Reactive Extensions (Rx) to collections, enabling declarative manipulation of in-memory data. It provides tools like SourceCache and SourceList to manage dynamic collections through operators such as filtering, sorting, grouping, and transforming. The library allows developers to maintain a primary data source and chain operators to create observable streams that automatically reflect changes.

Tokens
5.2K
Snippets
11
Records
35
Agent score
84%

What's inside Dynamic Data

  1. What is Dynamic Data?

    main

    Dynamic Data is a portable class library that brings the power of Reactive Extensions (Rx) to collections. It is designed to solve the complexity of managing dynamic collections that require filtering, sorting, grouping, transforming, or joining multiple data sources.

    Instead of manually managing collection updates, you maintain a primary data source (a SourceCache<TObject, TKey> or SourceList<TObject>) and then chain together declarative operators to shape the data. The resulting observable collections automatically reflect changes made to the underlying source.

  2. Understand the DynamicData branching and versioning model

    main

    DynamicData uses Nerdbank.GitVersioning (NBGV) to derive deterministic package versions from git history. The version is calculated based on the 'height' (the number of commits since the version field in version.json was last changed).

    Branching Model

    • main branch: Used for active development of the next minor or major version. Packages published from here are pre-releases (e.g., X.Y.0-preview.N).
    • release/<major>.x branches: Lifetime branches for a specific major version (e.g., release/9.x). Packages published from here are stable (e.g., X.Y.N).

    Every PR merged to either main or a release/ branch automatically triggers a NuGet package publication via the .github/workflows/release.yml workflow.

  3. How to use Dynamic Data collections and operators

    main

    The core workflow in Dynamic Data involves maintaining a data source and using the .Connect() method to create an observable stream of changes. You then apply operators to this stream to transform the data.

    Example workflow:

    1. Maintain a SourceCache<TObject, TKey> or SourceList<TObject>.
    2. Call .Connect() to get an IObservable<IChangeSet<...>>.
    3. Chain operators like .Filter(), .Transform(), .Sort(), and .ObserveOnDispatcher().
    4. Use .Bind(out collection) to link the processed stream to a read-only observable collection.
    5. Use .DisposeMany() if the transformed items are disposable to ensure proper cleanup.
    ReadOnlyObservableCollection<TradeProxy> list;
    
    var myTradeCache = new SourceCache<Trade, long>(trade => trade.Id);
    var myOperation = myTradeCache.Connect() 
    		.Filter(trade=>trade.Status == TradeStatus.Live) 
    		.Transform(trade => new TradeProxy(trade))
    		.Sort(SortExpressionComparer<TradeProxy>.Descending(t => t.Timestamp))
    		.ObserveOnDispatcher()
    		.Bind(out list) 
    		.DisposeMany()
    		.Subscribe()
  4. Compare ObservableList and ObservableCache

    main

    Choosing between SourceList and SourceCache depends on your data requirements:

    FeatureObservableList (SourceList)ObservableCache (SourceCache)
    UniquenessAllows duplicatesRequires a unique ID (dictionary-based)
    UpdatesNo concept of an 'update' notificationNotifies on adds, updates, and removes
    OperatorsStandard operatorsWider range of mature, key-based operators

    Rule of thumb: If your data has a unique identifier, use SourceCache to ensure no duplicates and to benefit from more advanced operators.

  5. Backport a fix from main to a release branch

    main

    If you need to cherry-pick a specific fix from main to a release branch without promoting the entire main branch, follow these steps:

    1. Checkout the release branch and pull latest:
      git checkout release/9.x && git pull
    2. Create a new fix branch from the release branch:
       ```bash
    git checkout -b fix/backport-XYZ release/9.x
    1. Cherry-pick the specific commit(s) from main:
      git cherry-pick <sha-on-main>
    4. Push the branch and open a PR targeting `release/9.x`.
    5. Merging the PR will trigger `release.yml` to publish the next patch.
    
    git checkout release/9.x && git pull
    git checkout -b fix/backport-XYZ release/9.x
    git cherry-pick <sha-on-main>
  6. Dependency Requirements: Rx and ReactiveUI

    main

    Dynamic Data has specific minimum version requirements for its dependencies:

    • Rx (System.Reactive): Minimum version 4.1.5 is required (as of v 6.10.0).
    • ReactiveUI: The DynamicData.ReactiveUI package requires minimum version v9.0.1 (as of v 6.6.0). Note that DynamicData.ReactiveUI is considered obsolete because it provides adapters for ReactiveList, which is no longer supported by the ReactiveUI team.
  7. Promote main to a stable minor version

    main

    When main is ready to become the next stable minor (e.g., moving from 9.4.x stable to 9.5.x stable), use the Promote main to stable minor workflow.

    Steps

    1. Run the Promote main to stable minor (promote-minor.yml) workflow from the GitHub Actions UI.
      • Inputs: target_release_branch (e.g., release/9.x) and stable_version (e.g., 9.5).
    2. Review and merge the two PRs created by the workflow:
      • Promotion PR: Contains the full diff of main since the last promotion. Review this carefully.
      • Main-bump PR: Moves main to the next preview version (e.g., 9.6-preview).
    3. CRITICAL: Merge the promotion PR first, then immediately merge the main-bump PR. Leaving the bump PR unmerged will cause subsequent pushes to main to fail the prerelease regression guard.
  8. Ship a stable patch or a preview version

    main

    Most routine updates do not require running manual workflows. They are handled via standard Pull Requests:

    Ship a stable patch (e.g., 9.4.42)

    1. Open a PR targeting the appropriate release branch (e.g., release/9.x).
    2. Merge the PR.
    3. release.yml will automatically publish the patch to NuGet.

    Ship a preview (e.g., 9.5.0-preview.42)

    1. Open a PR targeting main.
    2. Merge the PR.
    3. release.yml will automatically publish the pre-release to NuGet.
  9. Create derived collections from SourceList or SourceCache

    main

    You can create new observable collections by applying operators to an existing SourceList or SourceCache. This allows you to expose modified, filtered, or sorted views of your data to consumers without changing the original source.

    • Use .Filter() to create a subset of items.
    • Use .AsObservableList() on a SourceList to get a derived list.
    • Use .AsObservableCache() on a SourceCache to get a derived cache.
    • Use .BindToObservableList(out observableList) for both SourceList and SourceCache. This is recommended when using .AutoRefresh(), as standard collections do not support refresh notifications.
  10. Prepare for a breaking change on main

    main

    Before merging a PR that introduces a breaking change to main, you must bump main to the next major preview version.

    Steps

    1. Run the Bump main to next major preview (bump-major-preview.yml) workflow.
      • Inputs: next_major (e.g., 10). This must be exactly one greater than the current latest stable major.
    2. Merge the resulting PR. main will now publish as 10.0.0-preview.N.
    3. Label your breaking-change PR with the breaking-change label.
    4. Rebase your breaking-change PR onto the updated main (or merge main into it) so the PR head includes the new version.json from the bump. The PR version check will block the merge until these steps are completed.
  11. Cut a new major release

    main

    To ship the first stable version of a new major (e.g., moving from 9.x to 10.x), use the Cut major release workflow.

    Steps

    1. Run the Cut major release (cut-major.yml) workflow.
      • Inputs: major_version (e.g., 10).
      • Optional Input: next_main_version (e.g., 11.0).
    2. The workflow will:
      • Create a new release/10.x branch at version 10.0.
      • Open a main-bump PR to move main to the next version.
      • Dispatch release.yml to publish the first 10.0.x patch.
    3. Merge the main-bump PR immediately to avoid regression guard failures on main.
  12. Transform items in a change set

    main
    Use the Transform operator to map objects from the source stream to a different type. For hierarchical data, use TransformToTree (available for SourceCache only) to create a reactive tree structure. To flatten child collections, use TransformMany.