Troubleshooting & FAQ
Common issues encountered when working with RA.Utilities.Core.ValueObjects and guidance on how to resolve them.
Common Issues
Why did deserialization throw a JsonException instead of my value object type?
Cause: The built-in converter (ValueObjectJsonConverter<T>) catches validation failures during deserialization and wraps them in a JsonException. This is by design: malformed JSON inputs must not instantiate invalid domain objects.
try
{
var account = JsonSerializer.Deserialize<BankAccount>(json);
}
catch (JsonException ex)
{
// ex.Message contains the failed validation error details
}
Fix: Validate client input before passing it to deserialization, or use endpoint validation filters in your API framework.
Email.Parse throws even though the string "looks valid" — why?
Cause: Validation is performed on the trimmed, lowercased string. Common reasons for rejection:
- Value is whitespace or empty →
BaseErrorCode.EmailRequired(REQUIRED_EMAIL) - Exceeds 254 characters (RFC 5321 limit) →
BaseErrorCode.EmailMaxLength(EMAIL_MAX_LENGTH) - Missing
@, missing local part, missing domain, or domain lacks a dot →BaseErrorCode.EmailNotValid(INVALID_EMAIL)
Fix: Ensure the input matches the standard format (name@example.com). Refer to the Email API Reference.
Why did an IBAN throw BadRequestException with IbanCheckDigits?
Cause: The account number failed the ISO 7064 mod-97 checksum calculation. Even a single transposed or mistyped digit alters the checksum remainder.
// ❌ Check digits 00 do not match expected mod-97 check digits
new IBAN("GB00WEST12345698765432"); // throws BadRequestException (IbanCheckDigits)
// ✅ Check digits 82 match the mod-97 algorithm
new IBAN("GB82WEST12345698765432");
Fix: Verify that the account number was entered correctly by the user. The exception's ValidationError includes ExpectedValue with the correct check digits for that BBAN.
Why does IBAN accept a 20-character German IBAN if DE IBANs are 22 characters?
Cause: IBAN performs international structural validation (13 to 34 characters) and ISO 7064 mod-97 check digit verification. It does not hardcode per-country length registries into the core library, ensuring forward-compatibility with international banking changes.
Fix: If your application enforces national length rules, validate the length in your application layer using BaseErrorCode.IbanLength (IBAN_LENGTH):
using RA.Utilities.Core.Constants;
using RA.Utilities.Core.Exceptions;
using RA.Utilities.Core.ValueObjects;
var iban = new IBAN(input);
if (iban.CountryCode == "DE" && iban.Value.Length != 22)
{
throw new BadRequestException(new ValidationError(BaseErrorMessage.IbanLength)
{
PropertyName = "IBAN",
ErrorCode = BaseErrorCode.IbanLength,
AttemptedValue = iban.Value,
ExpectedValue = "22"
});
}
My two Currency or IBAN instances aren't equal — but the strings look the same?
Cause: Equality compares the normalized Value property. While whitespace and casing are normalized automatically:
- Different characters or invisible unicode characters will prevent equality.
- Currencies must match ISO 4217 uppercase strings.
var a = Currency.Parse("EUR", provider: null);
var b = new Currency(" eur ");
Console.WriteLine(a == b); // true — normalized to "EUR"
I get a BadRequestException from a constructor — is that expected?
Cause: Yes. Unlike standard .NET types that throw ArgumentException, value objects in this library throw BadRequestException populated with structured ValidationError records.
Fix: Let the exception bubble up to the RA.Utilities.Api global error handler, which converts it to a standard HTTP 400 Bad Request response. If checking user input conditionally without exceptions, use TryParse.
Can I store these value objects in Entity Framework Core?
Yes. Because string-backed value objects expose a single normalized Value property, configure them in EF Core with a simple value converter:
modelBuilder.Entity<Customer>(entity =>
{
entity.Property(e => e.Email)
.HasConversion(email => email.Value, str => new Email(str));
entity.Property(e => e.Iban)
.HasConversion(iban => iban.Value, str => new IBAN(str));
});
For Money, map the composite properties using EF Core's ComplexProperty or OwnsOne.
Why can't I assign a string directly to a value object parameter?
Cause: There is intentionally no implicit conversion from string to any value object. Allowing implicit conversion would bypass validation and reintroduce primitive obsession bugs.
Fix: Explicitly instantiate the value object or use Parse / TryParse:
// ❌ Does not compile by design
// Email email = "user@gmail.com";
// ✅ Explicit creation with boundary validation
Email email = new("user@gmail.com");
// ✅ Implicit conversion to string is supported (lossless)
string raw = email;
Known Limitations
- Format verification over registry checks:
Currencyvalidates that the code is exactly 3 characters; it does not check against a dynamic ISO currency list.IBANvalidates the 13–34 character structure and ISO 7064 mod-97 check digits; it does not hardcode per-country lengths or domestic clearing codes.
MoneyJSON serialization:Email,Currency,SSN, andIBANserialize as plain strings viaValueObjectJsonConverter<T>.Moneyhas two properties (AmountandCurrency) and serializes as a standard JSON object.- Normalization is irreversible: Trimming and canonical casing (lowercase for
Email, uppercase forCurrency,SSN, andIBAN, stripping separators forIBAN) modify input formatting permanently.
Performance Tips
- Use
TryParsefor user-facing API validation: When accepting input from untrusted clients where invalid input is expected, useTryParseto avoid exception throwing and stack trace generation. - Records are lightweight: Value objects are immutable sealed records with zero heap allocations beyond the backing string. Equality comparisons evaluate the underlying string.