Skip to content

Registration Builders

The MessageRegistrationBuilder provides a structured way to create message registrations with fine-grained control over lifecycle, configuration, and resource cleanup.

This guide covers:

  • When to use the builder pattern
  • Configuring registrations with options
  • Managing lifecycle with leases
  • Practical usage examples

Table of Contents


Overview

DxMessaging offers two approaches to message registration:

Approach Use Case
Direct token creation Simple scenarios with manual lifecycle management
Builder pattern Complex scenarios requiring DI integration, lifecycle hooks, or structured configuration

When to use the builder pattern

  • You need lifecycle callbacks (build, activate, deactivate, dispose)
  • You want to integrate with dependency injection containers
  • You prefer a more declarative configuration style
  • You need to manage multiple registrations as a single unit

When direct token creation is sufficient

  • Simple MonoBehaviour-based messaging
  • No need for lifecycle hooks
  • Using MessagingComponent or similar built-in components

MessageRegistrationBuilder

The MessageRegistrationBuilder class creates MessageRegistrationLease instances based on configuration options.

Creating a Builder

C#
using DxMessaging.Core.MessageBus;

// Builder that uses global bus resolution
MessageRegistrationBuilder builder = new MessageRegistrationBuilder();

// Builder with a custom provider
IMessageBusProvider provider = new FixedMessageBusProvider(myMessageBus);
MessageRegistrationBuilder builderWithProvider = new MessageRegistrationBuilder(provider);

Building Leases

Call Build() with options to create a lease:

C#
MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions
{
    ActivateOnBuild = true,
    Configure = token =>
    {
        _ = token.RegisterUntargeted<PlayerDamaged>(OnPlayerDamaged);
    }
};

using MessageRegistrationLease lease = builder.Build(options);

MessageRegistrationBuildOptions

Configure how the builder creates registrations using MessageRegistrationBuildOptions.

Owner Configuration

Specify who owns the registration:

C#
// Explicit InstanceId owner
options.Owner = new InstanceId(42);

// Unity Object owner (Unity 2021.3+)
options.UnityOwner = gameObject;  // or any Component

When omitted, a synthetic owner ID is generated automatically. UnityOwner takes precedence over Owner when both are set.

Message Bus Selection

Control which bus handles registrations:

C#
// Use a specific bus directly
options.PreferredMessageBus = myMessageBus;

// Use a provider (falls back if PreferredMessageBus is null)
options.MessageBusProvider = new FixedMessageBusProvider(otherBus);

Resolution order:

  1. PreferredMessageBus if set
  2. MessageBusProvider.Resolve() if provider is set
  3. Builder's provider (from constructor) if set
  4. null (uses global bus)

Handler State

Control the initial state of handlers:

C#
// Whether the underlying MessageHandler starts active (default: true)
options.HandlerStartsActive = true;

// Whether to call Enable() immediately after building (default: false)
options.ActivateOnBuild = true;

Diagnostics

Enable diagnostic mode for debugging:

C#
options.EnableDiagnostics = true;

This sets MessageRegistrationToken.DiagnosticMode to true, enabling detailed logging.

Configure Callback

Register handlers immediately after token creation:

C#
options.Configure = token =>
{
    _ = token.RegisterUntargeted<GameStarted>(OnGameStarted);
    // For targeted messages, provide the target InstanceId
    _ = token.RegisterTargeted<DamageMessage>(targetInstanceId, OnDamage);
};

This callback runs after the token is created but before lifecycle hooks and activation.

Note: RegisterTargeted<T> requires an InstanceId target parameter specifying which entity should receive the message. For Unity objects, use RegisterGameObjectTargeted<T> or RegisterComponentTargeted<T> instead.

Lifecycle Hooks

Add callbacks for lifecycle events:

C#
options.Lifecycle = new MessageRegistrationLifecycle(
    onBuild: token => Debug.Log("Token created"),
    onActivate: token => Debug.Log("Token enabled"),
    onDeactivate: token => Debug.Log("Token disabled"),
    onDispose: token => Debug.Log("Token disposed")
);

MessageRegistrationLease

A MessageRegistrationLease wraps the created token and provides lifecycle management.

Properties

Property Type Description
Token MessageRegistrationToken The underlying registration token
Handler MessageHandler The handler hosting registrations
MessageBus IMessageBus The bus used for registrations
Owner InstanceId The owner identifier
IsActive bool Whether the lease is currently active

Activation Methods

C#
// Enable the token and invoke OnActivate callback
lease.Activate();

// Disable the token and invoke OnDeactivate callback
lease.Deactivate();
  • Activate() throws ObjectDisposedException if called after disposal
  • Deactivate() is safe to call multiple times or after disposal

Dispose Pattern

Leases implement IDisposable:

C#
using MessageRegistrationLease lease = builder.Build(options);
// ... use the lease
// Automatically deactivated and disposed at end of scope

Disposal sequence:

  1. Calls Deactivate() if active (triggers OnDeactivate)
  2. Invokes OnDispose callback
  3. Marks lease as disposed

MessageRegistrationLifecycle

The MessageRegistrationLifecycle struct holds callbacks for each lifecycle stage:

C#
public readonly struct MessageRegistrationLifecycle
{
    public MessageRegistrationLifecycle(
        Action<MessageRegistrationToken> onBuild,
        Action<MessageRegistrationToken> onActivate,
        Action<MessageRegistrationToken> onDeactivate,
        Action<MessageRegistrationToken> onDispose);

    public Action<MessageRegistrationToken> OnBuild { get; }
    public Action<MessageRegistrationToken> OnActivate { get; }
    public Action<MessageRegistrationToken> OnDeactivate { get; }
    public Action<MessageRegistrationToken> OnDispose { get; }
}

Callback Order

  1. OnBuild — Immediately after lease creation, before activation
  2. OnActivate — When Activate() is called (or automatically if ActivateOnBuild = true)
  3. OnDeactivate — When Deactivate() is called or during disposal while active
  4. OnDispose — During Dispose(), after deactivation

All callbacks receive the MessageRegistrationToken as a parameter.


Code Examples

Basic Builder Usage

C#
using DxMessaging.Core.MessageBus;
using UnityEngine;

public sealed class BasicBuilderExample : MonoBehaviour
{
    private MessageRegistrationBuilder builder;
    private MessageRegistrationLease lease;

    private void Awake()
    {
        builder = new MessageRegistrationBuilder();

        MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions
        {
            UnityOwner = this,
            ActivateOnBuild = true,
            Configure = token =>
            {
                _ = token.RegisterUntargeted<GameEvent>(OnGameEvent);
            }
        };

        lease = builder.Build(options);
    }

    private void OnDestroy()
    {
        lease?.Dispose();
    }

    // Action<T> handler signature (used with RegisterUntargeted<T>(..., Action<T>))
    private void OnGameEvent(GameEvent message)
    {
        Debug.Log($"Received: {message}");
    }

    // Alternative: FastHandler<T> signature for better performance (avoids boxing)
    // private void OnGameEvent(ref GameEvent message)
    // {
    //     Debug.Log($"Received: {message}");
    // }
}

DI Integration Pattern

C#
using DxMessaging.Core.MessageBus;
using UnityEngine;

public sealed class DIIntegrationExample : MonoBehaviour
{
    // Injected by your DI container
    private IMessageBusProvider messageBusProvider;
    private MessageRegistrationLease lease;

    public void Initialize(IMessageBusProvider provider)
    {
        messageBusProvider = provider;

        MessageRegistrationBuilder builder = new MessageRegistrationBuilder(provider);

        MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions
        {
            UnityOwner = this,
            ActivateOnBuild = true,
            Configure = ConfigureRegistrations
        };

        lease = builder.Build(options);
    }

    private void ConfigureRegistrations(MessageRegistrationToken token)
    {
        _ = token.RegisterUntargeted<PlayerSpawned>(OnPlayerSpawned);
        _ = token.RegisterUntargeted<PlayerDied>(OnPlayerDied);
    }

    private void OnDestroy()
    {
        lease?.Dispose();
    }

    // Action<T> handler signatures
    private void OnPlayerSpawned(PlayerSpawned message)
    {
        Debug.Log($"Player spawned: {message.PlayerId}");
    }

    private void OnPlayerDied(PlayerDied message)
    {
        Debug.Log($"Player died: {message.PlayerId}");
    }
}

Custom Lifecycle Hooks

C#
using DxMessaging.Core.MessageBus;
using UnityEngine;

public sealed class LifecycleHooksExample : MonoBehaviour
{
    private MessageRegistrationBuilder builder;
    private MessageRegistrationLease lease;

    private void Awake()
    {
        builder = new MessageRegistrationBuilder();

        MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions
        {
            UnityOwner = this,
            HandlerStartsActive = true,
            ActivateOnBuild = false,  // We'll activate manually
            EnableDiagnostics = true,
            Configure = token =>
            {
                _ = token.RegisterUntargeted<LevelLoaded>(OnLevelLoaded);
            },
            Lifecycle = new MessageRegistrationLifecycle(
                onBuild: token => Debug.Log("[Lifecycle] Token built"),
                onActivate: token => Debug.Log("[Lifecycle] Activated - now receiving messages"),
                onDeactivate: token => Debug.Log("[Lifecycle] Deactivated - paused"),
                onDispose: token => Debug.Log("[Lifecycle] Disposed - cleanup complete")
            )
        };

        lease = builder.Build(options);
    }

    private void OnEnable()
    {
        // Activate when the component is enabled
        lease?.Activate();
    }

    private void OnDisable()
    {
        // Deactivate but don't dispose - we might re-enable
        lease?.Deactivate();
    }

    private void OnDestroy()
    {
        lease?.Dispose();
    }

    // Action<T> handler signature
    private void OnLevelLoaded(LevelLoaded message)
    {
        Debug.Log($"Level loaded: {message.LevelName}");
    }
}

FixedMessageBusProvider

For scenarios where you always want to use a specific bus instance:

C#
using DxMessaging.Core.MessageBus;

// Create a provider that always returns a specific bus
IMessageBus myBus = new MessageBus();
IMessageBusProvider provider = new FixedMessageBusProvider(myBus);

// Use with builder
MessageRegistrationBuilder builder = new MessageRegistrationBuilder(provider);

// Or override per-build
MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions
{
    MessageBusProvider = provider,
    Configure = token => { /* ... */ }
};

See Also