Skip to content

Compute Services: Cheat Sheet

Quick reference for Fusion core concepts.

Configuration

Register Fusion and compute services:

cs
var fusion = services.AddFusion();
fusion.AddService<ICartService, CartService>();
// or: fusion.AddComputeService<CartService>();

Change global computed options defaults:

cs
ComputedOptions.Default = ComputedOptions.Default with {
    MinCacheDuration = TimeSpan.FromSeconds(30),
};

Configure invalidation source tracking (for debugging):

cs
Invalidation.TrackingMode = InvalidationTrackingMode.WholeChain;

Compute Service Interface

cs
public interface ICartService : IComputeService
{
    [ComputeMethod]
    Task<List<Order>> GetOrders(long cartId, CancellationToken cancellationToken = default);
}

Compute Service Implementation

cs
public class CartService : ICartService
{
    // Must be virtual + return Task<T>
    [ComputeMethod]
    public virtual async Task<List<Order>> GetOrders(long cartId, CancellationToken cancellationToken)
    {
        // Implementation
        return new List<Order>();
    }
}

ComputeMethod Attribute Options

cs
[ComputeMethod(
    MinCacheDuration = 60,              // Keep in memory for 60 seconds
    AutoInvalidationDelay = 300,        // Auto-refresh every 5 minutes
    TransientErrorInvalidationDelay = 5, // Retry errors after 5 seconds
    InvalidationDelay = 0.5,            // Debounce invalidations by 500ms
    ConsolidationDelay = 0)]            // Invalidate only when value changes
public virtual async Task<Data> GetData() { return default!; }

Invalidation

Invalidate from a regular method:

cs
using (Invalidation.Begin()) {
    _ = service.GetOrders(cartId, default);
}

Invalidate a Computed<T> directly:

cs
computed.Invalidate();
computed.Invalidate(TimeSpan.FromSeconds(30));  // Delayed
computed.Invalidate(new InvalidationSource("reason"));  // With source

Working with Computed<T>

Capture:

cs
var computed1 = await Computed.Capture(() => service.GetData(id, cancellationToken));
var computed2 = await Computed.TryCapture(() => service.GetData(id, default));  // Returns null on failure

Get existing (without triggering computation):

cs
var existing = Computed.GetExisting(() => service.GetData(id, default));

Get current computed inside a compute method:

cs
var computed = Computed.GetCurrent();
var computedTyped = Computed.GetCurrent<Data>();  // Typed, throws if null

Check consistency:

cs
if (computed.IsConsistent()) { /* ... */ }
if (computed.IsInvalidated()) { /* ... */ }

Update (recompute):

cs
var newComputed = await computed.Update(cancellationToken);

Use as dependency:

cs
Data value1 = await computed.Use(cancellationToken);
Data value2 = await computed.Use(allowInconsistent: true, cancellationToken);  // Allow stale

Await invalidation:

cs
await computed.WhenInvalidated(cancellationToken);
computed.Invalidated += c => Console.WriteLine("Invalidated!");

Await until condition is met:

cs
var computed = await Computed.Capture(() => service.GetCount(id, cancellationToken));
computed = await computed.When(count => count >= 10, cancellationToken);

Observe changes:

cs
var computed = await Computed.Capture(() => service.GetValue(id, cancellationToken));
await foreach (var c in computed.Changes(cancellationToken)) {
    Console.WriteLine($"New value: {c.Value}");
}

Deconstruct for pattern matching:

cs
var (value, error) = computed;

Isolate from dependency tracking:

cs
using (Computed.BeginIsolation()) {
    // Calls here won't register as dependencies
}

ComputedRegistry

cs
ComputedRegistry.InvalidateEverything();  // Useful for tests
await ComputedRegistry.Prune();           // Force prune dead entries

States

Get StateFactory:

cs
var stateFactory = services.StateFactory();
// or: StateFactory.Default (for tests)

Create mutable state:

cs
var state = stateFactory.NewMutable<int>(initialValue: 0);

state.Set(42);           // Set value
state.Value = 42;        // Same as above
var value1 = state.Value; // Read value
var value2 = await state.Use(cancellationToken); // Use in compute methods

Create computed state:

cs
using var computedState = stateFactory.NewComputed(
    new ComputedState<string>.Options() {
        InitialValue = "",
        UpdateDelayer = FixedDelayer.Get(1), // 1 second delay
        EventConfigurator = state => {
            state.Updated += (s, _) => Console.WriteLine($"Updated: {s.Value}");
        },
    },
    async (state, cancellationToken) => {
        var data = await service.GetData(cancellationToken);
        return data.ToString()!;
    });

await computedState.Update(); // Wait for first computation
var value = computedState.Value;

State properties:

cs
var computed = state.Computed;           // Current Computed<T>
var snapshot = state.Snapshot;           // Immutable snapshot
var lastGood = state.LastNonErrorValue;  // Last value before error

Update Delayers

cs
var d1 = FixedDelayer.Get(1);    // 1 second delay
var d2 = FixedDelayer.Get(0.5);  // 500ms delay
var d3 = FixedDelayer.NextTick;  // ~16ms delay
var d4 = FixedDelayer.MinDelay;  // Minimum safe delay (32ms)

State Events

cs
state.Invalidated += (s, kind) => { /* ... */ };
state.Updating += (s, kind) => { /* ... */ };
state.Updated += (s, kind) => { /* ... */ };