-
Notifications
You must be signed in to change notification settings - Fork 0
Advanced 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
- Overview
- MessageRegistrationBuilder
- MessageRegistrationBuildOptions
- MessageRegistrationLease
- MessageRegistrationLifecycle
- Code Examples
- See Also
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 |
- 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
- Simple MonoBehaviour-based messaging
- No need for lifecycle hooks
- Using
MessagingComponentor similar built-in components
The MessageRegistrationBuilder class creates MessageRegistrationLease instances based on configuration options.
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);Call Build() with options to create a lease:
MessageRegistrationBuildOptions options = new MessageRegistrationBuildOptions
{
ActivateOnBuild = true,
Configure = token =>
{
_ = token.RegisterUntargeted<PlayerDamaged>(OnPlayerDamaged);
}
};
using MessageRegistrationLease lease = builder.Build(options);Configure how the builder creates registrations using MessageRegistrationBuildOptions.
Specify who owns the registration:
// Explicit InstanceId owner
options.Owner = new InstanceId(42);
// Unity Object owner (Unity 2021.3+)
options.UnityOwner = gameObject; // or any ComponentWhen omitted, a synthetic owner ID is generated automatically. UnityOwner takes precedence over Owner when both are set.
Control which bus handles registrations:
// Use a specific bus directly
options.PreferredMessageBus = myMessageBus;
// Use a provider (falls back if PreferredMessageBus is null)
options.MessageBusProvider = new FixedMessageBusProvider(otherBus);Resolution order:
-
PreferredMessageBusif set -
MessageBusProvider.Resolve()if provider is set - Builder's provider (from constructor) if set
-
null(uses global bus)
Control the initial state of handlers:
// Whether the underlying MessageHandler starts active (default: true)
options.HandlerStartsActive = true;
// Whether to call Enable() immediately after building (default: false)
options.ActivateOnBuild = true;Enable diagnostic mode for debugging:
options.EnableDiagnostics = true;This sets MessageRegistrationToken.DiagnosticMode to true, enabling detailed logging.
Register handlers immediately after token creation:
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.
Add callbacks for lifecycle events:
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")
);A MessageRegistrationLease wraps the created token and provides lifecycle management.
| 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 |
// Enable the token and invoke OnActivate callback
lease.Activate();
// Disable the token and invoke OnDeactivate callback
lease.Deactivate();-
Activate()throwsObjectDisposedExceptionif called after disposal -
Deactivate()is safe to call multiple times or after disposal - If
ActivateOnBuildactivation throws, the builder disposes the partially constructed lease before throwing again. If cleanup itself cannot finish because deregistration remains retryable,Build()throwsMessageRegistrationBuildException; disposeexception.Leaseafter resolving the cleanup failure.
Leases implement IDisposable:
using MessageRegistrationLease lease = builder.Build(options);
// ... use the lease
// Automatically deactivated and disposed at end of scopeDisposal sequence:
- Calls
Deactivate()if active (triggersOnDeactivate) - Invokes
OnDisposecallback - Clears the owned token's staged registrations and token-local diagnostics
- Marks the lease as disposed after token cleanup succeeds
Token cleanup still runs when a lifecycle callback throws; Dispose() rethrows the first lifecycle exception after cleanup. If the bus deregistration action itself throws before cleanup, the failed token cleanup remains retryable by calling Dispose() again.
The MessageRegistrationLifecycle struct holds callbacks for each lifecycle stage:
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; }
}-
OnBuild-- Immediately after lease creation, before activation -
OnActivate-- WhenActivate()is called (or automatically ifActivateOnBuild = true) -
OnDeactivate-- WhenDeactivate()is called or during disposal while active -
OnDispose-- DuringDispose(), after deactivation
All callbacks receive the MessageRegistrationToken as a parameter.
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}");
// }
}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}");
}
}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}");
}
}For scenarios where you always want to use a specific bus instance:
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 => { /* ... */ }
};- Message-Bus-Providers -- More on the provider system
- Runtime-Configuration -- Dynamic reconfiguration options
- Getting-Started-Overview
- Getting-Started-Getting-Started
- Getting-Started-Install
- Getting-Started-Quick-Start
- Getting-Started-Visual-Guide
- Concepts-Message-Types
- Concepts-Listening-Patterns
- Concepts-Targeting-And-Context
- Concepts-Interceptors-And-Ordering
- Guides-Patterns
- Guides-Unity-Integration
- Guides-Testing
- Guides-Diagnostics
- Guides-Advanced
- Guides-Migration-Guide
- Advanced-Emit-Shorthands
- Advanced-Message-Bus-Providers
- Advanced-Runtime-Configuration
- Advanced-String-Messages
- Reference-Reference
- Reference-Quick-Reference
- Reference-Helpers
- Reference-Faq
- Reference-Glossary
- Reference-Troubleshooting
- Reference-Compatibility
Links