GenerateCodeForTryFrom - place GenerateNullCheckAndReturnXxx() after GenerateCallToNormalize/Validate and before new instance creation
Describe the feature
The GenerateNullCheckAndReturnXxx() does not allow value object authors to implement custom handling of null value (e.g. custom validation message, default value object instance creation, etc.). It just provides default handling (even default error message that can't be customized)
I agree that GenerateNullCheckAndReturnXxx() ensures the value object won't be created with a null value.
If only we do this check as the last mile, before new instance creation, it could provide value object authors with more flexibility during normalization/validation.
Hi @shupoval - thanks for the feedback! I'll take a look into extending this. Have you got an idea of how you'd like it to look?
Hi @SteveDunn,
thank you for considering this.
The main idea behind this enhancement is to postpone the default behavior when the null value is used as an argument to the TryFrom() methods and let the author of the value type handle the null value first and only apply default handling if an author has done nothing to it.
Also, it seems like the From() method does have a bug in recent release(s) in case the null value is used and no custom validation is provided.
Please see the code sample below for more details:
using Vogen;
var csv1 = CustomStringValue.TryFrom(null);
Console.WriteLine($"csv1: {(csv1.IsSuccess ? csv1.ValueObject : csv1.Error.ErrorMessage)}");
// BUG: Does not throw.
var csv2 = CustomStringValue.From(null);
//Console.WriteLine($"csv2: {csv2}"); // BUG: Default handling is not applied in From() cases. Throws NRE on Value.ToString()
// Results into default NULL value handling with "The value provided was null" error message.
// The intent here is to first let value object author handle NULL and if it wasn't handled then fallback to default handling
var csv3 = CustomStringValueWithCustomNullValidation.TryFrom(null);
Console.WriteLine($"csv3: {(csv3.IsSuccess ? csv3.ValueObject : csv3.Error.ErrorMessage)}");
// Throws OK: value object author NULL handling was used
//var csv4 = CustomStringValueWithCustomNullValidation.From(null);
//Console.WriteLine($"csv4: {csv4}");
// Results into default NULL value handling with "The value provided was null" error message.
// NormalizeInput wasn't invoke and that is why no way for value object author to use some default value here
var csv5 = CustomStringValueWithDefault.TryFrom(null);
Console.WriteLine($"csv5: {(csv5.IsSuccess ? csv5.ValueObject : csv5.Error.ErrorMessage)}");
// BUG: the same as for csv2 described above
var csv6 = CustomStringValueWithDefault.From(null);
//Console.WriteLine($"csv6: {csv6}");
Console.WriteLine("done");
[ValueObject<string>]
public readonly partial record struct CustomStringValue;
[ValueObject<string>]
public readonly partial record struct CustomStringValueWithCustomNullValidation
{
private static Validation Validate(string? input)
=> input switch
{
null or "" => Validation.Invalid($"The value of {nameof(CustomStringValueWithCustomNullValidation)} type is required and cannot be blank"),
_ => Validation.Ok
};
}
[ValueObject<string>]
public readonly partial record struct CustomStringValueWithDefault
{
public static CustomStringValueWithDefault Default = new("DEFAULT_VALUE");
private static string NormalizeInput(string? input)
=> input switch
{
null or "" => Default.Value,
_ => input
};
}