如何在FluentValidation中为未指定规则的属性设置默认验证规则?
Absolutely! You can set up default validation rules for properties that don't have explicit rules defined in FluentValidation. For your MyClass scenario—where Bar needs a specific NotNull() rule (allowing empty strings) and all other string properties (Foo, Baz) should use NotEmpty()—here's a clean, maintainable way to pull it off:
Step 1: Keep Your Class Definition
First, your existing class stays exactly as is:
public class MyClass { public string Foo { get; set; } public string Bar { get; set; } public string Baz { get; set; } }
Step 2: Build a Validator with Automatic Default Rules
Instead of manually adding NotEmpty() to every string property except Bar, we'll create a validator that automatically applies the default rule to any unconfigured string properties. Here's the implementation:
using FluentValidation; using System.Linq; using System.Reflection; using System.Linq.Expressions; public class MyClassValidator : AbstractValidator<MyClass> { public MyClassValidator() { // First, define your explicit, property-specific rule for Bar RuleFor(c => c.Bar).NotNull(); // Get names of all properties that already have rules configured var configuredProps = Descriptor.GetMembersWithRules() .Select(member => member.Member.Name) .ToHashSet(); // Grab all string properties on MyClass that don't have explicit rules var unconfiguredStringProps = typeof(MyClass).GetProperties() .Where(prop => prop.PropertyType == typeof(string) && !configuredProps.Contains(prop.Name)) .Select(prop => prop.Name); // Apply the default NotEmpty() rule to each unconfigured property foreach (var propName in unconfiguredStringProps) { // Dynamically build the lambda expression needed for RuleFor var param = Expression.Parameter(typeof(MyClass), "c"); var property = Expression.Property(param, propName); var ruleExpression = Expression.Lambda<Func<MyClass, string>>(property, param); RuleFor(ruleExpression).NotEmpty(); } } }
How This Works
Let's break down the logic:
- Explicit Rules First: We start with the specific rule for
Bar—this ensures it gets excluded from the default rule logic. - Track Configured Properties: Using the validator's
Descriptorproperty, we pull out all properties that already have rules attached. - Find Unconfigured String Properties: We use reflection to get all string properties on
MyClass, then filter out any that already have explicit rules. - Apply Default Rule: For each unconfigured property, we dynamically create the lambda expression needed for
RuleForand attach theNotEmpty()rule.
Bonus: Reusable Extension Method
If you want to reuse this default rule logic across multiple validators, wrap it in an extension method:
public static class ValidatorExtensions { public static void ApplyDefaultStringNotEmptyRule<T>(this AbstractValidator<T> validator) { var configuredProps = validator.Descriptor.GetMembersWithRules() .Select(member => member.Member.Name) .ToHashSet(); var unconfiguredStringProps = typeof(T).GetProperties() .Where(prop => prop.PropertyType == typeof(string) && !configuredProps.Contains(prop.Name)) .Select(prop => prop.Name); foreach (var propName in unconfiguredStringProps) { var param = Expression.Parameter(typeof(T), "c"); var property = Expression.Property(param, propName); var ruleExpression = Expression.Lambda<Func<T, string>>(property, param); validator.RuleFor(ruleExpression).NotEmpty(); } } }
Then use it in your validator like this:
public MyClassValidator() { RuleFor(c => c.Bar).NotNull(); this.ApplyDefaultStringNotEmptyRule(); }
Key Notes
- Order Matters: Always define your explicit rules before applying the default rule logic—this ensures explicit rules take precedence.
- Reflection Overhead: This approach uses reflection, which has minimal performance impact for most business scenarios. For high-throughput environments, consider caching the property lookup results.
- Flexibility: Adjust the default rule (e.g., switch to
NotNull()) or expand the property filter (e.g., include value types) to fit your specific needs.
内容的提问来源于stack exchange,提问作者Andrew Cooper

