using System.Collections.Generic;
using System.Text.RegularExpressions;
using FluentValidation.Resources;
using FluentValidation.Validators;
public static void Main()
Console.WriteLine("Hel1lo World");
public class EldReportData
public HeaderSection HeaderSection { get; set; }
public CMVSection CMVSection { get; set; }
public PowerUpDownSection PowerUpDownSection { get; set; }
public class HeaderSection
public PowerUnitLine PowerUnitLine { get; set; }
public class PowerUnitLine
public string CMVVin { get; set;}
public List<CMVLine> CMVs { get; set; }
public string CMVVin { get; set; }
public class PowerUpDownSection
public List<PowerUpDownLine> PowerUpDowns { get; set; }
public class PowerUpDownLine : IVehicleIdentifier
public int VehicleId {get; set; }
public string CMVVin { get; set;}
public interface IVehicleIdentifier
int VehicleId {get; set;}
string CMVVin { get; set; }
public class VehicleInfoValidator : AbstractValidator<IVehicleIdentifier>
public VehicleInfoValidator()
RuleSet("VIN, Default", () => {
.SetValidator(new VinValidator(), opt => opt
.WithErrorCode("VIN-ERROR-CODE"));
public class VinValidator : AbstractValidator<IVehicleIdentifier>
.Cascade(CascadeMode.StopOnFirstFailure)
.Must(HaveValidCharsAndSize)
.Must(HaveValidCheckDigit);
private const string VALID_CHARS_PATTERN = "^\\-?(?:(?![IOQ])[A-Z0-9]){17}$";
private static readonly Regex DigitsAndLengthValidator = new Regex(VALID_CHARS_PATTERN, RegexOptions.Compiled);
private static bool HaveValidCharsAndSize(IVehicleIdentifier vehicleInfo, string vin,
PropertyValidatorContext context)
const string INVALID_CHARS_MSG =
"{PropertyName} it's not a valid VIN. A VIN must contains 17 characters, " +
"all capital letters, between A-Z and 0-9 excluding I, O, Q.";
context.Rule.CurrentValidator.Options.ErrorMessageSource = new StaticStringSource(INVALID_CHARS_MSG);
return DigitsAndLengthValidator.IsMatch(vin);
private static readonly Dictionary<char, int> VinLetterMapping = new Dictionary<char, int>
{'A', 1}, {'B', 2}, {'C', 3}, {'D', 4}, {'E', 5}, {'F', 6}, {'G', 7}, {'H', 8},
{'J', 1}, {'K', 2}, {'L', 3}, {'M', 4}, {'N', 5}, {'P', 7}, {'R', 9},
{'S', 2}, {'T', 3}, {'U', 4}, {'V', 5}, {'W', 6}, {'X', 7}, {'Y', 8}, {'Z', 9}
private static readonly int[] VinDigitValue = { 8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2 };
private static bool HaveValidCheckDigit(IVehicleIdentifier vehicleInfo, string vin, PropertyValidatorContext context)
const string INVALID_VIN_DIGIT_MSG = "{PropertyName} does not have a valid VIN check-digit. " +
"See 49 C.F.R. sect. 565 and ISO 3779:2009 for information on the VIN format.";
context.Rule.CurrentValidator.Options.ErrorMessageSource = new StaticStringSource(INVALID_VIN_DIGIT_MSG);
vin = vin.TrimStart('-');
for (int i = 0; i < vin.Length; i++)
int value = Char.IsLetter(vinDigit) ? VinLetterMapping[vin[i]] : vinDigit - '0';
total += value * VinDigitValue[i];
int checkDigitValue = total % 11;
string checkDigit = checkDigitValue < 10 ? checkDigitValue.ToString() : "X";
return vin[8].ToString() == checkDigit;
public const string VIN = "VIN";
public static class FluentValidationExtensions
public static IRuleBuilderOptions<T, TProperty> SetValidator<T, TProperty>(this IRuleBuilderInitial<T, TProperty> rule,
AbstractValidator<TProperty> validator, Action<ValidatorOptions<T, TProperty>> optionsSetup, params string[] ruleSets)
return SetupValidator(validator, optionsSetup, rule, ruleSets);
public static IRuleBuilderOptions<T, TProperty> SetValidator<T, TProperty>(
this IRuleBuilderOptions<T, TProperty> rule,
AbstractValidator<TProperty> validator, Action<ValidatorOptions<T, TProperty>> optionsSetup, params string[] ruleSets)
return SetupValidator(validator, optionsSetup, rule, ruleSets);
private static IRuleBuilderOptions<T, TProperty> SetupValidator<T, TProperty>(
AbstractValidator<TProperty> validator,
Action<ValidatorOptions<T, TProperty>> optionsSetup,
IRuleBuilder<T, TProperty> ruleBuilder,
params string[] ruleSets)
var childValidator = new ChildValidatorAdaptor(validator, validator.GetType())
if (optionsSetup == null)
return ruleBuilder.SetValidator(childValidator);
var validatorOptions = new ValidatorOptions<T, TProperty>(childValidator);
optionsSetup.Invoke(validatorOptions);
var options = childValidator.Options;
foreach (var validationRule in validator)
foreach (var propertyValidator in validationRule.Validators)
if (validatorOptions.OverridesMessage)
propertyValidator.Options.ErrorMessageSource = options.ErrorMessageSource;
if (options.ErrorCodeSource != null)
propertyValidator.Options.ErrorCodeSource = options.ErrorCodeSource;
propertyValidator.Options.CustomStateProvider = options.CustomStateProvider;
propertyValidator.Options.Severity = options.Severity;
return ruleBuilder.SetValidator(childValidator);
public class ValidatorOptions<T, TProperty>
private readonly IPropertyValidator _validator;
public bool OverridesMessage { get; private set; }
public ValidatorOptions(IPropertyValidator validator)
public ValidatorOptions<T, TProperty> WithMessage(string errorMessage)
_validator.Options.ErrorMessageSource = new StaticStringSource(errorMessage);
public ValidatorOptions<T, TProperty> WithMessage(Func<T, string> messageProvider)
_validator.Options.ErrorMessageSource = new LazyStringSource(ctx => messageProvider((T)ctx.InstanceToValidate));
public ValidatorOptions<T, TProperty> WithMessage(Func<T, TProperty, string> messageProvider)
_validator.Options.ErrorMessageSource = new LazyStringSource(context =>
messageProvider((T)context.InstanceToValidate, (TProperty)context.PropertyValue));
public ValidatorOptions<T, TProperty> WithErrorCode(string errorCode)
_validator.Options.ErrorCodeSource = new StaticStringSource(errorCode);
public ValidatorOptions<T, TProperty> WithState(Func<T, object> stateProvider)
_validator.Options.CustomStateProvider = ctx => stateProvider((T)ctx.Instance);
public ValidatorOptions<T, TProperty> WithState(Func<T, TProperty, object> stateProvider)
_validator.Options.CustomStateProvider = ctx =>
stateProvider((T)ctx.Instance, (TProperty)ctx.PropertyValue);
public ValidatorOptions<T, TProperty> WithSeverity(Severity severity)
_validator.Options.Severity = severity;