Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fluent validation: set custom message on custom validation

Scenario

I have a custom rule to validate the shipping cost of an order:

public class OrderValidator : BaseValidator<Order>
{

    private string CustomInfo { get; set; }

    public OrderValidator()
    {
        //here I call the custom validation method and I try to add the CustomInfo string in the message
        RuleFor(order => order.ShippingCost).Cascade(CascadeMode.StopOnFirstFailure).NotNull().Must(
            (order, shippingCost) => CheckOrderShippingCost(order, shippingCost)
        ).WithMessage("{PropertyName} not set or not correct: {PropertyValue}." + (String.IsNullOrEmpty(CustomInfo) ? "" : " " + CustomInfo));
    }

    //this is the custom validation method
    private bool CheckOrderShippingCost(Order o, decimal shippingCost)
    {
        bool res = false;

        try
        {
            /*
             * check the actual shippingCost and set the res value
             */
        }
        catch (Exception ex)
        {
            CustomInfo = ex.ToString();
            res = false;
        }

        return res;
    }
}

In case of exception, I store the exception info into the CustomInfo private member and I add it to the validation message.

Then I run the validator:

OrderValidator oVal = new OrderValidator();
oVal.Results = oVal.Validate(order);
if (!oVal.Results.IsValid)
    oVal.Results.Errors.ForEach(delegate(ValidationFailure error) {
        Console.WriteLine(error.ErrorMessage);
    });

Issue

Everything works right, in case of exception the CustomInfo is properly set to the ex.ToString() value. But eventually the error message displayed in the console does NOT show the CustomInfo, but only the first part of the message:

      "Shipping Cost not set or not correct: 5.9"

Question

Why the custom message does not contains the CustomInfo string? Is it possible to add the exception info the the custom message in another way?

like image 936
Alberto De Caro Avatar asked Aug 05 '14 09:08

Alberto De Caro


1 Answers

According to this https://fluentvalidation.codeplex.com/wikipage?title=Customising&referringTitle=Documentation&ANCHOR#CustomError

you should rather use

.WithMessage("{PropertyName} not set or not correct: {PropertyValue}. {0}", order => order.CustomInfo);

which would require that your CustomInfo on the level of the Order class, rather than your validator class

EDIT

You could use:

public static class OrderExtensions
{
    private static IDictionary<Order,string> customErrorMessages;

    public static void SetError(this Order order, string message) {
        if (customErrorMessages == null) {
            customErrorMessages = new Dictionary<Order,string>();
        }
        if (customErrorMessages.ContainsKey(order)) {
            customErrorMessages[order] = message;
            return;
        }
        customErrorMessages.Add(order, message);
    }

    public static string GetError(this Order order) {
        if (customErrorMessages == null || !customErrorMessages.ContainsKey(order)) {
            return string.Empty;
        }
        return customErrorMessages[order];
    }
}

with some small changes to your validator

public class OrderValidator : BaseValidator<Order>
{
    public OrderValidator()
    {
        //here I call the custom validation method and I try to add the CustomInfo string in the message
        RuleFor(order =>     order.ShippingCost).Cascade(CascadeMode.StopOnFirstFailure).NotNull().Must(
            (order, shippingCost) => CheckOrderShippingCost(order, shippingCost)
        ).WithMessage("{PropertyName} not set or not correct: {PropertyValue}. {0}", order => order.GetError()));
    }

    //this is the custom validation method
    private bool CheckOrderShippingCost(Order o, decimal shippingCost)
    {
        bool res = false;

        try
        {
            /*
             * check the actual shippingCost and set the res value
             */
        }
        catch (Exception ex)
        {
            order.SetError(ex.ToString());
            res = false;
        }
        return res;
    }
}
like image 114
Icepickle Avatar answered Nov 03 '22 22:11

Icepickle