DevBitsLab.Feeds

A reactive data management library for .NET that provides state-aware feeds with automatic loading, error handling, and incremental change tracking for collections.

Overview

DevBitsLab.Feeds simplifies working with asynchronous data in .NET applications. It provides a unified approach to handling loading states, errors, and data updates through a composable feed abstraction.

Key Features

  • Simplified State Management — Automatic tracking of loading, error, and value states
  • Reactive Updates — Event-driven architecture for real-time UI synchronization
  • Incremental Collection Updates — Efficient list change tracking for optimal UI performance
  • Type-Safe Composition — Combine multiple data sources with compile-time type checking
  • MVVM-Ready — Built-in support for ICommand integration and data binding

Installation

Package Manager Console

Install-Package DevBitsLab.Feeds

.NET CLI

dotnet add package DevBitsLab.Feeds

PackageReference

<PackageReference Include="DevBitsLab.Feeds" Version="1.0.0" />

Requirements

Requirement Supported Versions
.NET 8.0, 10.0
C# 10+ (12+ recommended)

Note: C# 13+ enables partial property support for automatic [BindableFeed] initialization. On earlier versions, call InitializeBindableFeeds() in your constructor.

Quick Start

1. Add the namespace

using DevBitsLab.Feeds;

2. Create a Feed

// Create a feed that auto-loads
var userFeed = Feed<User>.Create(async ct => 
    await userService.GetCurrentUserAsync(ct));

// Subscribe to state changes
userFeed.StateChanged += (s, e) => {
    if (userFeed.HasValue) DisplayUser(userFeed.Value!);
    else if (userFeed.IsLoading) ShowSpinner();
    else if (userFeed.HasError) ShowError(userFeed.Error!);
};

// Or simply await
var user = await userFeed;

List Feed with Change Tracking

var productsFeed = ListFeed<Product>.Create(async ct => 
    await productApi.GetAllAsync(ct));

productsFeed.ItemsChanged += (s, e) => {
    switch (e.Change) {
        case ListChange<Product>.ItemAdded a: 
            InsertRow(a.Index, a.Item); 
            break;
        case ListChange<Product>.ItemRemoved r: 
            RemoveRow(r.Index); 
            break;
        case ListChange<Product>.ItemUpdated u: 
            UpdateRow(u.Index, u.NewItem); 
            break;
    }
};

Editable State with Validation

var settingsState = State<Settings>.Create(
    loadFunc: LoadSettingsAsync,
    saveFunc: SaveSettingsAsync,
    validateFunc: s => s?.Theme == null 
        ? ValidationResult.Error("Theme", "Required") 
        : ValidationResult.Success);

settingsState.Update(settings => settings with { Theme = "Dark" });

if (settingsState.IsDirty && !settingsState.HasErrors)
    await settingsState.SaveAsync();

Documentation

Section Description
Articles Conceptual guides and tutorials
API Reference Auto-generated API documentation

Architecture

┌─────────────────────────────────────────────────────────┐
│                    IFeed<T>                             │
│  (State management, events, pause/resume)               │
├────────────────────┬────────────────────────────────────┤
│                    │                                    │
│    Feed<T>         │        IListFeed<T>                │
│   (Single value)   │    (Collection + changes)          │
│         │          │              │                     │
│         ▼          │              ▼                     │
│    State<T>        │        ListFeed<T>                 │
│  (Edit/validate)   │   (Incremental updates)            │
│                    │                                    │
└────────────────────┴────────────────────────────────────┘
         │                         │
         └─────────┬───────────────┘
                   ▼
           CombinedFeed<S,R>
         (Multiple sources → one result)