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
-
ScalarValueObject<TSelf, decimal>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 validationTryCreate(decimal)- Factory method for decimals (required by IScalarValue)TryCreate(decimal?, string?)- Factory method with zero validation and custom field nameTryCreate(string?, string?)- Factory method for parsing strings with validationIParsable<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
valuedecimalThe 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 validationTryCreate(decimal?, string?)- Create from nullable decimal with validationTryCreate(string?, string?)- Create from string with validation