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, callInitializeBindableFeeds()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)