Skip to main content

Troubleshooting & FAQ

Common Issues​

How do I serialize ResponseType as a string instead of a number?​

Cause: ResponseType is a plain enum with no built-in converter. System.Text.Json therefore writes its numeric value (ResponseType.NotFound → 8) by default.

Fix: Register JsonStringEnumConverter on the serializer your endpoints use:

using System.Text.Json.Serialization;

// Minimal APIs and Results.Json (what RA.Utilities.Api uses)
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

// MVC controllers
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

With the converter, ResponseType.NotFound is written as "NotFound". Note that the converter is stricter than the old ResponseTypeJsonConverter: unknown names and null for a non-nullable enum throw JsonException instead of falling back to ResponseType.Error.

See ResponseType for the full behavior matrix.

Can I use ResponseType in a switch expression?​

Yes — fully. ResponseType is a standard enum, so constant patterns work and the compiler can verify exhaustiveness:

// ✅ Constant patterns (preferred)
var label = responseType switch
{
ResponseType.Success => "OK",
ResponseType.NotFound => "Not Found",
ResponseType.Conflict => "Conflict",
ResponseType.BadRequest => "Bad Request",
ResponseType.Unauthorized => "Unauthorized",
_ => "Other"
};

How do I compare two ResponseType values?​

Use == or .Equals() — enum members are value types, so comparison is simple and allocation-free:

ResponseType a = ResponseType.NotFound;
ResponseType b = ResponseType.NotFound;

bool same = a == b; // true
bool sameEq = a.Equals(b); // true

Can I create a ResponseType from a string at runtime?​

Yes, with the standard System.Enum APIs:

// ✅ Parse a member name
ResponseType type = Enum.Parse<ResponseType>("NotFound", ignoreCase: true);

// ✅ Safer for untrusted input — numeric strings parse to undefined values
if (Enum.TryParse(value, ignoreCase: true, out ResponseType parsed) && Enum.IsDefined(parsed))
{
// safe to use parsed
}

Enum.TryParse succeeds for any name, including one that is not a ResponseType member, and for numeric strings. Always combine it with Enum.IsDefined when the input is untrusted.

Why can't I use BaseResponseCode values as default parameter values?​

Cause: Since v10.0.4 the fields are static readonly, not const. C# requires a compile-time constant for a default parameter value, so void Fail(int code = BaseResponseCode.BadRequest) fails with CS1736.

Fix: Use a nullable sentinel plus a coalesce, or add an overload:

// ✅ Nullable sentinel
public IActionResult Fail(string? message = null)
{
return BadRequest(message ?? BaseResponseMessages.BadRequest);
}

// ✅ Overload
public IActionResult Fail() => Fail(BaseResponseMessages.BadRequest);
public IActionResult Fail(string message) => BadRequest(message);

Why did private const string X = BaseResponseMessages.NotFound; stop compiling?​

Cause: A const initializer must itself be a compile-time constant. The package fields are now static readonly, so the compiler reports CS0133.

Fix: Make your declaration static readonly:

// ❌ CS0133
private const string NotFoundMessage = BaseResponseMessages.NotFound;

// ✅
private static readonly string NotFoundMessage = BaseResponseMessages.NotFound;

Why can't I use these values in attributes or case labels?​

Cause: Attribute arguments and case labels also require compile-time constants. Referencing a static readonly field produces CS0182 (attribute argument) or CS0150 (case label).

Fix: Use a local const or a literal in those positions, or replace the case with an if:

// Attribute — define the literal in your own project
private const string NotFoundMessage = "The requested resource was not found.";

[DefaultValue(NotFoundMessage)]
public string? Message { get; set; }

// case label -> if
if (error.ErrorCode == BaseErrorCode.EmailRequired)
{
ShowEmailError();
}

These are the only source-breaking contexts. Ordinary reads such as BaseResponseCode.NotFound compile unchanged.

Which IBAN error code should I use?​

Use the code that matches the check that failed:

CheckError codeMessage
Missing or whitespace inputBaseErrorCode.IbanRequiredBaseErrorMessage.IbanRequired
Structure: country code, check digits, or 13–34 charactersBaseErrorCode.IbanNotValidBaseErrorMessage.IbanNotValid
ISO 7064 mod-97 check digitsBaseErrorCode.IbanCheckDigitsBaseErrorMessage.IbanCheckDigits
Country-specific total lengthBaseErrorCode.IbanLengthBaseErrorMessage.IbanLength
using RA.Utilities.Core.Constants;
using RA.Utilities.Core.Exceptions;

throw new BadRequestException(new ValidationError(BaseErrorMessage.IbanNotValid)
{
PropertyName = "IBAN",
ErrorCode = BaseErrorCode.IbanNotValid,
AttemptedValue = normalizedIban,
ExpectedValue = "2a2n[9-30c] in 2 to 7 groups of 3 to 5 characters"
});

The IBAN value object picks the first three automatically. IbanLength is for your own country-registry checks; see below.

When should I use IBAN_LENGTH instead of NOT_VALID_IBAN?​

Cause: RA.Utilities.Core.ValueObjects.IBAN performs structural validation only — a two-letter country code, two check digits, and a 9–30 character BBAN (13–34 characters in total) — followed by the ISO 7064 mod-97 test. It does not consult a per-country length table, so an IBAN that is structurally valid but the wrong length for its country (for example, 20 characters for DE, which expects 22) passes.

Fix: If your domain requires country-specific lengths, validate them separately and report BaseErrorCode.IbanLength:

// DE expects 22 characters in total
if (compactIban.StartsWith("DE", StringComparison.Ordinal) && compactIban.Length != 22)
{
throw new BadRequestException(new ValidationError(BaseErrorMessage.IbanLength)
{
PropertyName = "IBAN",
ErrorCode = BaseErrorCode.IbanLength,
AttemptedValue = compactIban.Length,
ExpectedValue = 22
});
}

See Validate an IBAN and pick the right error code for a registry-based helper.

Does BaseResponseCode include Created?​

Yes. BaseResponseCode.Created is 201, alongside Success, Accepted, NoContent, and the error codes. Use it when you construct a response manually; ASP.NET Core's Created and CreatedAtAction helpers already set 201, so you usually do not need the field there.

Why can I write new BaseResponseCode()?​

Cause: Since v10.0.4 the constant holders are partial class declarations rather than static class declarations, so they have an implicit public parameterless constructor. They hold only static state.

Answer: Don't instantiate them. Access the fields through the type name (BaseResponseCode.NotFound). Instantiating the class compiles but has no purpose.

Can I add my own values to these classes with partial?​

No. The partial keyword only permits splitting a type's definition inside the assembly that declares it. BaseResponseCode and the other types are compiled into RA.Utilities.Core.Constants; you cannot add a partial part from your own project. Define your own constants class for domain-specific values.


Known Limitations​

  • Closed set of values: Consuming projects cannot add new ResponseType members. For an outcome without a dedicated member (e.g., BadGateway), use the closest built-in member (typically Error or Problem) and communicate the specifics through the error code and message.
  • Numeric JSON by default: Without JsonStringEnumConverter, responseType is serialized as a number. Register the converter if your clients depend on names such as "NotFound".
  • Strict string deserialization with the converter: JsonStringEnumConverter throws JsonException for unknown names and for null when the target enum is non-nullable. Validate first if you need tolerant input.
  • default is Success: default(ResponseType) and uninitialized fields are ResponseType.Success (value 0). Use explicit members in models where Success is not the right default.
  • No implicit conversion from string: Use Enum.TryParse/Enum.Parse or the built-in members directly.
  • No compile-time constants: Since v10.0.4 the fields are static readonly. They cannot be used in attributes, default parameter values, case labels, or const declarations.
  • No localization support: BaseResponseMessages provides English-only strings. For multi-language support, use these as fallback defaults and implement your own localization layer.
  • IBAN validation is structural: IBAN checks the 13–34 character structure and the ISO 7064 mod-97 check digits, not a per-country length table. Report BaseErrorCode.IbanLength from your own registry check if your domain needs it.

Performance Tips​

  • Static field reads are cheap: BaseResponseCode.NotFound is a single static field load — no allocation, no computation.
  • Values are not inlined: Unlike the previous const fields, the JIT reads the value from the package assembly. The overhead is negligible in request-handling code, but extremely hot loops that compare thousands of values may benefit from a local variable.
  • Enum members are values: ResponseType.NotFound involves no allocation, and ToString() returns the member name.
  • Prefer TryParse over Parse: When the input may be invalid, Enum.TryParse avoids exception overhead on the failure path.
  • Configure the converter once: Register JsonStringEnumConverter in startup rather than creating JsonSerializerOptions per request.