Skip to the content.

Fusion Cheat Sheet

Compute Services

Compute service interface:

// IComputeService is just an optional tagging interface.
// Nevertheless, we highly recommend to "implement" it -
// it allows you to use a few extension methods, such as
// .GetServices() and .GetCommander().
public interface ICartService : IComputeService
{
    // When applied to interface method, this attribute 
    // is "inherited" by the implementation
    [ComputeMethod]
    Task<List<Order>> GetOrders(long cartId, CancellationToken cancellationToken = default);
}

Compute service implementation:

public class CartService : ICartService 
{
    // The method must be virtual + return Task<T>
    public virtual async Task<List<Order>> GetOrders(long cartId, CancellationToken cancellationToken)
    {
        // Implementation goes here
    }
}    

Add invalidation logic (after any code that changes the result of what’s invalidated inside the using block below):

using (Computed.Invalidate()) {
    // Whatever compute method calls you make inside this block
    // are invalidating calls. Instead of running the actual method 
    // code they invalidate the result of this call.
    // They always complete synchronously, and you can pass
    // "default" instead of any CancellationToken here.
    _ = GetOrders(cartId, default);
}    

Register compute service:

fusion = services.AddFusion(); // services is IServiceCollection
fusion.AddService<ICartService, CartService>();

Compute Service Clients

Configure Fusion RPC client (this has to be done once in a code that configures client-side IServiceProvider):

var baseUri = new Uri("http://localhost:5005");

var fusion = services.AddFusion();
fusion.Rpc.AddWebSocketClient(baseUri);

Register Compute Service client:

fusion.AddClient<ICartService>();

Use use it:

// Just call it the same way you call the original one.
// Any calls that are expected to produce the same result
// as the previous call with the same arguments will
// be resolved via locally cached IComputed.
// When a IComputed gets invalidated on the server,
// Fusion will invalidate its replica on every client.

Commander

Declare command type:

// You don't have to use records, but we recommend to use
// immutable types for commands and outputs of compute methods
public record UpdateCartCommand(long CartId, Dictionary<long, long?> Updates) 
    : ICommand<Unit> // Unit is command's return type; you can use any other
{
    // Compatibility: Newtonsoft.Json needs this constructor to deserialize the record
    public UpdateCartCommand() : this(0, null!) { }
}

Add command handler in the compute service interface:

public interface ICartService : IComputeService
{
    // ...
    [CommandHandler] // This attribute is also "inherited" by the impl.
    Task<Unit> UpdateCart(UpdateCartCommand command, CancellationToken cancellationToken = default);

Add command handler implementation:

public class CartService : ICartService 
{
    // Must be virtual + return Task<T> for ICommand<T>;
    // Command must be its first argument; other arguments are resolved
    // from DI container - except CancellationToken, which is 
    // passed directly.
    public virtual Task<Unit> UpdateCart(UpdateCartCommand command, CancellationToken cancellationToken) 
    {
        if (Computed.IsInvalidating()) {
            // Write the invalidation logic for this command here.
            //
            // A set of command handlers registered by Fusion will
            // "retry" this handler inside the invalidation block
            // once its "normal" logic completes successfully.
            // Moreover, they'll run this block on every node on the 
            // cluster if you use multi-host invalidation.
            return default;
        }

        // Command handler code goes here
    }

Register command handler:

// Nothing is needed for handlers declared inside compute services

Working with IComputed

Capture:

var computed = await Computed.Capture(() => service.ComputeMethod(args, cancellationToken));

Check whether IComputed is still consistent:

if (computed.IsConsistent()) {
    // ...
}

Await for invalidation:

// Always pass CancellationToken here, otherwise you'll
// end up with a memory leak due to growing number of
// event handler registrations. They'll be gone on
// invalidation, of course, but what if it never happens?
await computed.WhenInvalidated(cancellationToken);
// Or
computed.Invalidated += c => Console.WriteLine("Invalidated!");

To be continued.

> Back to Tutorial | Documentation Home