DevBitsLab.Feeds
0.0.2
See the version list below for details.
dotnet add package DevBitsLab.Feeds --version 0.0.2
NuGet\Install-Package DevBitsLab.Feeds -Version 0.0.2
<PackageReference Include="DevBitsLab.Feeds" Version="0.0.2" />
<PackageVersion Include="DevBitsLab.Feeds" Version="0.0.2" />
<PackageReference Include="DevBitsLab.Feeds" />
paket add DevBitsLab.Feeds --version 0.0.2
#r "nuget: DevBitsLab.Feeds, 0.0.2"
#:package DevBitsLab.Feeds@0.0.2
#addin nuget:?package=DevBitsLab.Feeds&version=0.0.2
#tool nuget:?package=DevBitsLab.Feeds&version=0.0.2
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 is a reactive data management library that 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 Benefits
- 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
๐ Documentation
๐ Full API Documentation โ Detailed guides for all classes and interfaces
| Quick Links | |
|---|---|
| Feed<T> | Single-value reactive feed |
| ListFeed<T> | Reactive list with change tracking |
| State<T> | Editable state with validation |
| CombinedFeed | Combine multiple feeds |
๐ Requirements
- .NET 10.0 or later
- C# 14 (Preview)
- Nullable Reference Types enabled
๐ฆ Installation
.NET CLI
dotnet add package DevBitsLab.Feeds
Package Manager Console
Install-Package DevBitsLab.Feeds
๐ Getting Started
Basic Feed Usage
using DevBitsLab.Feeds;
// Create a feed that loads data from an API
var customerFeed = Feed<Customer>.Create(async ct =>
{
return await httpClient.GetFromJsonAsync<Customer>($"/api/customers/{id}", ct);
});
// Subscribe to state changes
customerFeed.StateChanged += (sender, e) =>
{
if (customerFeed.IsLoading)
ShowLoadingSpinner();
else if (customerFeed.HasValue)
DisplayCustomer(customerFeed.Value!);
else if (customerFeed.HasError)
ShowError(customerFeed.Error!.Message);
};
// Or await the feed directly
var customer = await customerFeed;
List Feed with Change Tracking
var productsFeed = ListFeed<Product>.Create(async ct =>
{
return await productApi.GetProductsAsync(ct);
});
// Subscribe to incremental changes
productsFeed.ItemsChanged += (sender, e) =>
{
switch (e.Change)
{
case ListChange<Product>.ItemAdded added:
InsertRowAt(added.Index, added.Item);
break;
case ListChange<Product>.ItemRemoved removed:
RemoveRowAt(removed.Index);
break;
}
};
// Modify the list - changes are tracked automatically
productsFeed.Add(new Product { Name = "New Product" });
Editable State with Validation
var settingsState = State<Settings>.Create(
loadFunc: async ct => await settingsService.LoadAsync(ct),
saveFunc: async (settings, ct) => await settingsService.SaveAsync(settings, ct),
validateFunc: settings =>
{
var result = new ValidationResult();
if (string.IsNullOrWhiteSpace(settings?.Username))
result.AddError(nameof(Settings.Username), "Username is required");
return result;
});
// Edit and save
settingsState.Update(settingsState.Value! with { Theme = "Dark" });
if (settingsState.IsDirty && !settingsState.HasErrors)
await settingsState.SaveAsync();
๐ง Core Types
| Type | Description |
|---|---|
Feed<T> |
Reactive feed for single values with async loading |
ListFeed<T> |
Reactive list with incremental change notifications |
State<T> |
Editable wrapper with dirty tracking and validation |
ValidationResult |
Property-level validation errors |
CombinedFeed |
Combines multiple feeds into one |
๐ Feed States
| State | Description |
|---|---|
None |
Initial state - no data loaded |
Loading |
Currently fetching data |
HasValue |
Data successfully loaded |
HasError |
Error occurred during loading |
Refreshing |
Updating existing data |
Retrying |
Retrying after error |
๐ API Quick Reference
Feed Methods
Create(loadFunc)- Create auto-loading feedCreateDeferred(loadFunc)- Create manually-triggered feedFromValue(value)- Create feed with existing valueFromError(exception)- Create feed in error stateRefreshAsync()- Reload data from sourceSelect(selector)- Transform feed value
ListFeed Methods
Empty()- Create empty list feedFromItems(items)- Create from existing itemsAdd(item)/Insert(index, item)- Add itemsRemove(item)/RemoveAt(index)- Remove itemsUpdate(index, newItem)- Replace itemBatchUpdate(action)- Atomic multi-operation
State Methods
Update(value)- Set new value (marks dirty)SaveAsync()- Persist changesRevertAsync()- Discard changesReset(value)- Set value without dirty flag
LINQ Extensions
Select(selector)- Project itemsWhere(predicate)- Filter itemsOrderBy(keySelector)- Sort itemsTake(count)/Skip(count)- PaginationAggregate(aggregator)- Compute aggregate value
โ Best Practices
1. Always Dispose Feeds
public class MyViewModel : IDisposable
{
private readonly Feed<Data> _feed;
public void Dispose() => _feed?.Dispose();
}
2. Batch Multiple List Changes
// Good: Single notification
listFeed.BatchUpdate(editor =>
{
editor.Add(item1);
editor.Add(item2);
});
3. Combine Feeds for Derived Data
var dashboardFeed = userFeed.CombineWith(
ordersFeed,
(user, orders) => new Dashboard(user, orders));
๐ Benchmark Results
Benchmarks run nightly. Run configuration: .NET 10, Release mode, Ubuntu Latest.
See the main repository README for full benchmark details.
Feed Throughput
| Operation | Mean | Throughput | Allocated |
|---|---|---|---|
| Access Feed.Value | 0.002 ns | ~500B ops/sec | - |
| Access Feed.HasValue | 0.23 ns | ~4.4B ops/sec | - |
| Feed.UpdateValue | 1.95 ns | ~513M ops/sec | - |
| Feed.Pause/Resume | 1.97 ns | ~508M ops/sec | - |
| Create deferred Feed | 20.19 ns | ~50M ops/sec | 120 B |
| Create Feed.FromValue | 33.92 ns | ~29M ops/sec | 208 B |
| Feed await (cached) | 39.95 ns | ~25M ops/sec | 176 B |
| Feed.Select transformation | 82.05 ns | ~12M ops/sec | 360 B |
ListFeed Throughput
| Operation | Mean | Throughput | Allocated |
|---|---|---|---|
| Access ListFeed.Count | 9.67 ns | ~103M ops/sec | - |
| Access ListFeed.Value | 10.47 ns | ~96M ops/sec | - |
| Create ListFeed.Empty | 23.40 ns | ~43M ops/sec | 168 B |
| Create FromItems (10) | 34.68 ns | ~29M ops/sec | 232 B |
| Create FromItems (100) | 49.33 ns | ~20M ops/sec | 592 B |
| Create FromItems (1000) | 248.35 ns | ~4M ops/sec | 4,192 B |
| ListFeed.Update | 2.45 ยตs | ~408K ops/sec | - |
| ListFeed.Add | 3.30 ยตs | ~303K ops/sec | - |
| ListFeed.BatchUpdate (10 ops) | 7.02 ยตs | ~142K ops/sec | - |
State Throughput
| Operation | Mean | Throughput | Allocated |
|---|---|---|---|
| State.MarkDirty | 0.08 ns | ~12.5B ops/sec | - |
| Access State.IsDirty | 0.24 ns | ~4.2B ops/sec | - |
| Access State.Value | 0.89 ns | ~1.1B ops/sec | - |
| State.Update (same value) | 1.17 ns | ~855M ops/sec | - |
| State.GetErrors | 22.07 ns | ~45M ops/sec | 64 B |
| Create State.FromValue | 129.91 ns | ~7.7M ops/sec | 736 B |
| State.Reset | 2.71 ยตs | ~369K ops/sec | - |
| State.Update (new value) | 3.79 ยตs | ~264K ops/sec | - |
| State.Update with validation | 8.64 ยตs | ~116K ops/sec | - |
Key Performance Characteristics
- Zero-allocation reads: Accessing
Value,State,IsDirty,HasValueallocates nothing - Sub-nanosecond property access: Core read operations complete in under 1 ns
- Efficient updates: Single-item operations complete in nanoseconds
- Scalable collections:
ListFeed<T>operations scale well with collection size - Batch optimization:
BatchUpdateamortizes notification overhead across multiple operations
๐ค Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
- Clone the repository
- Open in Visual Studio 2022 or later
- Build the solution
- Run tests
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on DevBitsLab.Feeds:
| Package | Downloads |
|---|---|
|
DevBitsLab.Feeds.WinUI
WinUI extensions for DevBitsLab.Feeds including FeedPresenter control for automatic feed state presentation and ISupportIncrementalLoading support for virtualized list loading. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial release of DevBitsLab.Feeds with Feed<T>, ListFeed<T>, State<T>, and CombinedFeed support.