Command Context
The CommandContext is a core component of the non-controller-based command pipeline in Cratis Arc. It provides contextual information and values that are available throughout the command execution lifecycle.
Overview
Section titled “Overview”The CommandContext is a record that encapsulates all the necessary information about a command being executed:
public record CommandContext( CorrelationId CorrelationId, Type Type, object Command, IEnumerable<object> Dependencies, CommandContextValues Values, ValidationResultSeverity? AllowedSeverity = default, object? Response = default);Properties
Section titled “Properties”- CorrelationId: A unique identifier for tracking the command execution across the system
- Type: The type of the command being executed
- Command: The actual command instance
- Dependencies: The resolved dependencies required to handle the command
- Values: A collection of key-value pairs providing additional context
- Response: The response, if any, that is returned as part of the command result.
Command Context Values
Section titled “Command Context Values”The Values property is a CommandContextValues instance that acts as a case-insensitive dictionary of contextual information. These values are populated through implementations of ICommandContextValuesProvider.
How Values Are Populated
Section titled “How Values Are Populated”The command pipeline uses the CommandContextValuesBuilder to collect values from all registered ICommandContextValuesProvider implementations.
Each provider receives the command instance being executed and contributes its values. If there are overlapping keys, the last provider’s value takes precedence.
Extending with Custom Values
Section titled “Extending with Custom Values”To add your own values to the command context, implement the ICommandContextValuesProvider interface:
public interface ICommandContextValuesProvider{ CommandContextValues Provide(object command);}The command parameter provides access to the command instance being executed, allowing providers to customize their values based on the specific command type or content.
Example Implementation
Section titled “Example Implementation”Here’s an example of a custom provider that adds audit tracking information:
public class AuditContextValuesProvider : ICommandContextValuesProvider{ private readonly IDateTimeProvider _dateTimeProvider; private readonly IHttpContextAccessor _httpContextAccessor;
public AuditContextValuesProvider(IDateTimeProvider dateTimeProvider, IHttpContextAccessor httpContextAccessor) { _dateTimeProvider = dateTimeProvider; _httpContextAccessor = httpContextAccessor; }
public CommandContextValues Provide(object command) { var values = new CommandContextValues();
values["ExecutedAt"] = _dateTimeProvider.UtcNow; values["ExecutedBy"] = _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? "System"; values["TraceId"] = Activity.Current?.TraceId.ToString() ?? Guid.NewGuid().ToString();
// Example of using command information values["CommandType"] = command.GetType().Name;
return values; }}Registration
Section titled “Registration”The provider will be automatically discovered and registered by the dependency injection system if it’s in the application’s assembly, or you can register it manually:
services.AddSingleton<ICommandContextValuesProvider, AuditContextValuesProvider>();Accessing Command Context
Section titled “Accessing Command Context”Within command handlers, filters, or other components in the command pipeline, you can access the current command context through the ICommandContextAccessor:
[Command]public record MyCommand(string SomeProperty){ public object Handle(ICommandContextAccessor contextAccessor) { // Access values from the current context var context = contextAccessor.Current; if (context.Values.TryGetValue("ExecutedBy", out var executedBy)) { // Use the execution user information for logging or business logic }
return new { Success = true }; }}Non-Controller-Based Pipeline
Section titled “Non-Controller-Based Pipeline”The CommandContext is specifically designed for the non-controller-based command pipeline. In this pipeline:
- Commands are executed through the
ICommandPipeline - The context is created automatically with resolved dependencies
- Values are populated from all registered providers
- The context is made available throughout the execution chain
- Filters can inspect and modify the execution based on context values
- Response value handlers can use context information for processing results
This differs from the controller-based approach where ASP.NET Core’s built-in dependency injection and model binding handle much of the context management.
Best Practices
Section titled “Best Practices”- Keep value providers lightweight and fast
- Use descriptive keys for your context values
- Avoid storing large objects in context values
- Consider the lifetime of your providers (typically singleton)
- Handle cases where expected values might not be present
- Use the context values for cross-cutting concerns like auditing, logging, and authorization