Custom Data Types
Custom data types let you define reusable metadata and rules once, then apply them consistently across many properties.
Why create one?
Create a custom data type when a value represents a distinct business concept, not just a CLR primitive.
Examples:
ProperNounProductCodePercentDocumentNumber
Basic inheritance
[DataAnnotations("Multi-line text", ["MaxLength(4000)"])]
public partial class MultilineText : Text
{
}
Derived types inherit parent annotations by default.
Override annotations intentionally
If you need different limits/formatting than the base type, define those annotations on the derived type.
[DataAnnotations("Short code", ["MaxLength(30)"])]
public partial class ShortCode : Text
{
}
Keep overrides explicit so the effective rules are easy to reason about.
Add reusable logic
Use data type events when behavior should apply to every property using the type.
[Logic]
public class ProductCodeBL(ProductCode.Logic productCode)
{
[RegisterLogic]
public void Normalize()
{
productCode.AutoCorrect().Transform(value => value?.Trim().ToUpperInvariant());
}
[RegisterLogic]
public void Validate()
{
productCode.Validate()
.RejectIf(value => string.IsNullOrWhiteSpace(value))
.WithMessage("Product code is required");
}
}
Guidance
- Keep type names semantic and business-focused.
- Use data type logic for cross-entity consistency.
- Use property logic when a rule is entity-specific.