Table of Contents

Class RequiredDecimal<TSelf>

Namespace
Trellis
Assembly
Trellis.Primitives.dll

Base class for creating strongly-typed decimal value objects that cannot have the default (zero) value. Provides a foundation for monetary amounts, percentages, and other domain concepts represented by decimals.

public abstract class RequiredDecimal<TSelf> : ScalarValueObject<TSelf, decimal>, IComparable<ValueObject>, IEquatable<ValueObject>, IConvertible where TSelf : RequiredDecimal<TSelf>, IScalarValue<TSelf, decimal>

Type Parameters

TSelf
Inheritance
RequiredDecimal<TSelf>
Implements
Inherited Members
Extension Methods

Examples

Creating a strongly-typed monetary value:

// Define the value object (partial keyword enables source generation)
public partial class UnitPrice : RequiredDecimal<UnitPrice>
{
}

// The source generator automatically creates:
// - IScalarValue<UnitPrice, decimal> interface implementation
// - public static Result<UnitPrice> TryCreate(decimal value, string? fieldName = null)
// - public static Result<UnitPrice> TryCreate(decimal? value, string? fieldName = null)
// - public static Result<UnitPrice> TryCreate(string? value, string? fieldName = null)
// - public static UnitPrice Parse(string s, IFormatProvider? provider)
// - public static bool TryParse(string? s, IFormatProvider? provider, out UnitPrice result)
// - public static explicit operator UnitPrice(decimal value)
// - private UnitPrice(decimal value) : base(value) { }

// Usage examples:

// Create from existing decimal with validation
var result1 = UnitPrice.TryCreate(19.99m);
// Returns: Success(UnitPrice) if value != 0
// Returns: Failure(ValidationError) if value == 0

// Create from string with validation
var result2 = UnitPrice.TryCreate("19.99");
// Returns: Success(UnitPrice) if valid decimal format
// Returns: Failure(ValidationError) if invalid format or zero

// With custom field name for validation errors
var result3 = UnitPrice.TryCreate(input, "product.price");
// Error field will be "product.price" instead of default "unitPrice"

Multiple strongly-typed decimal values in the same domain:

public partial class Price : RequiredDecimal<Price> { }
public partial class TaxRate : RequiredDecimal<TaxRate> { }
public partial class DiscountAmount : RequiredDecimal<DiscountAmount> { }

public class OrderLine
{
    public Price UnitPrice { get; }
    public TaxRate Tax { get; }

    // Compiler prevents mixing values:
    // ApplyDiscount(price); // Won't compile when DiscountAmount expected!
}

Remarks

This class extends ScalarValueObject<TSelf, T> to provide a specialized base for decimal-based value objects with automatic validation that prevents zero/default decimals. When used with the partial keyword, the PrimitiveValueObjectGenerator source generator automatically creates:

  • IScalarValue<TSelf, decimal> implementation for ASP.NET Core automatic validation
  • TryCreate(decimal) - Factory method for decimals (required by IScalarValue)
  • TryCreate(decimal?, string?) - Factory method with zero validation and custom field name
  • TryCreate(string?, string?) - Factory method for parsing strings with validation
  • IParsable<T> implementation (Parse, TryParse)
  • JSON serialization support via ParsableJsonConverter<T>
  • Explicit cast operator from decimal
  • OpenTelemetry activity tracing

Common use cases:

  • Monetary amounts (Price, Amount, Balance)
  • Rates and percentages (InterestRate, TaxRate)
  • Measurements requiring precision (Weight, Distance)
  • Any domain concept requiring a non-zero decimal value

Benefits over plain decimals:

  • Type safety: Cannot accidentally use Price where TaxRate is expected
  • Validation: Prevents zero/default decimals at creation time
  • Domain clarity: Makes code more self-documenting and expressive
  • Serialization: Consistent JSON and database representation

Constructors

RequiredDecimal(decimal)

Initializes a new instance of the RequiredDecimal<TSelf> class with the specified decimal value.

protected RequiredDecimal(decimal value)

Parameters

value decimal

The decimal value. Must not be zero.

Remarks

This constructor is protected and should be called by derived classes. When using the source generator (with partial keyword), a private constructor is automatically generated that includes validation.

Direct instantiation should be avoided. Instead, use the generated factory methods:

  • TryCreate(decimal, string?) - Create from decimal with validation
  • TryCreate(decimal?, string?) - Create from nullable decimal with validation
  • TryCreate(string?, string?) - Create from string with validation

See Also

ScalarValueObject<TSelf, T>
RequiredGuid<TSelf>
RequiredString<TSelf>
RequiredInt<TSelf>