Using FluentValidation's WithMessage method with a list of named parameters

后端 未结 5 637
长发绾君心
长发绾君心 2021-02-07 04:57

I am using FluentValidation and I want to format a message with some of the object\'s properties value. The problem is I have very little experience with expressions and delegat

5条回答
  •  北恋
    北恋 (楼主)
    2021-02-07 05:38

    You can't do that with the WithMessage in FluentValidation but you can high-jack the CustomState property and inject your message there. Here is a working example; Your other option is to fork FluentValidation and make an additional overload for the WithMethod.

    This is a console application with references to FluentValidation from Nuget and the JamesFormater from this blog post:

    http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx

    The Best answer. Took inspiration from Ilya and realized you can just piggyback off the extension method nature of fluent validation. So The below works with no need to modify anything in the library.

    using System;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    using System.Web;
    using System.Web.UI;
    using FluentValidation;
    
    namespace stackoverflow.fv
    {
        class Program
        {
            static void Main(string[] args)
            {
                var target = new My() { Id = "1", Name = "" };
                var validator = new MyValidator();
                var result = validator.Validate(target);
    
                foreach (var error in result.Errors)
                    Console.WriteLine(error.ErrorMessage);
    
                Console.ReadLine();
            }
        }
    
        public class MyValidator : AbstractValidator
        {
            public MyValidator()
            {
                RuleFor(x => x.Name).NotEmpty().WithNamedMessage("The name {Name} is not valid for Id {Id}");
            }
        }
    
        public static class NamedMessageExtensions
        {
            public static IRuleBuilderOptions WithNamedMessage(
                this IRuleBuilderOptions rule, string format)
            {
                return rule.WithMessage("{0}", x => format.JamesFormat(x));
            }
        }
    
        public class My
        {
            public string Id { get; set; }
            public string Name { get; set; }
        }
    
        public static class JamesFormatter
        {
            public static string JamesFormat(this string format, object source)
            {
                return FormatWith(format, null, source);
            }
    
            public static string FormatWith(this string format
                , IFormatProvider provider, object source)
            {
                if (format == null)
                    throw new ArgumentNullException("format");
    
                List values = new List();
                string rewrittenFormat = Regex.Replace(format,
                  @"(?\{)+(?[\w\.\[\]]+)(?:[^}]+)?(?\})+",
                  delegate(Match m)
                  {
                      Group startGroup = m.Groups["start"];
                      Group propertyGroup = m.Groups["property"];
                      Group formatGroup = m.Groups["format"];
                      Group endGroup = m.Groups["end"];
    
                      values.Add((propertyGroup.Value == "0")
                        ? source
                        : Eval(source, propertyGroup.Value));
    
                      int openings = startGroup.Captures.Count;
                      int closings = endGroup.Captures.Count;
    
                      return openings > closings || openings % 2 == 0
                         ? m.Value
                         : new string('{', openings) + (values.Count - 1)
                           + formatGroup.Value
                           + new string('}', closings);
                  },
                  RegexOptions.Compiled
                  | RegexOptions.CultureInvariant
                  | RegexOptions.IgnoreCase);
    
                return string.Format(provider, rewrittenFormat, values.ToArray());
            }
    
            private static object Eval(object source, string expression)
            {
                try
                {
                    return DataBinder.Eval(source, expression);
                }
                catch (HttpException e)
                {
                    throw new FormatException(null, e);
                }
            }
        }
    }
    
        

    提交回复
    热议问题