目录
译者注:老外为什么这么能侃大山?啰嗦到用脚能抠出三房两厅……
在验证方面,流畅验证(Fluent Validation)与数据注释(Data Annotations),孰优?这一问题时常置于开发者面前,本文将简单回答这个问题。
什么是流畅验证
再深入探讨流畅验证(Fluent Validation)之前,有必要了解一下流畅验证的起源与能力。
简介
Fluent Validation 是基于流畅接口(fluent interface)构建的强类型验证规则库(作者比喻为一搜避开小行星的宇宙飞船),是极客玩家们喜欢的方式。比如,验证对象成员:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Name).NotEmpty(); // Ensure the customer name is not an empty string
RuleFor(customer => customer.Age).InclusiveBetween(18, 60); // Ensure the customer age falls between 18 and 60
}
}
上例演示了使用 Fluent Validation 为 Customer
实体设置的基本规则,每条规则都清晰可辨,工程师一目了然。
好处
有以下几点:
-
可定制
-
允许复杂的验证规则
-
可与 ASP.NET 集成
常见场景
有时需要对单个属性进行复杂验证(比如对 18 岁或以上年龄者、且拥有有效保单的 Customer 进行验证),此时 Fluent Validation 很适合:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.HasInsurancePolicy).Equal(true);
RuleFor(customer => customer.Age).GreaterThanOrEqualTo(18);
}
}
什么是数据注释
数据注释(Data Annotations)与 Fluent Validation 截然不同。
数据注释的简单介绍
本质上讲,Data Annotations 是可以放置在领域模型(domain model)中用于指示其行为的属性,使其成为代码的“指南”,让代码变得更有可读性。除了代码的组织之外,数据注释更透明且易于识别,简化调试。如,在 Customer 类中使用简单的数据注释进行验证:
public class Customer
{
[Required(ErrorMessage = "Customer Name is required")]
public string Name { get; set; }
[Range(18, 60, ErrorMessage = "Age should be between 18 and 60")]
public int Age { get; set; }
}
这段代码展示了如何通过 Data Annotations 将验证规则直接应用于属性 Name 和 Age。每个注释都直接与其验证的属性相邻,使得代码的可读性大大提高。
数据注释的优点
Data Annotations 有以下三点优点:
-
当场声明:使模型一目了然
-
简单性:没有引入额外的验证类,只是在类上附加验证规则
-
无缝:可完美嵌入到 ASP.NET MVC 之中
数据注释的高级用法
数据注释不仅可以对字段进行简单检查,还可以结合 ORM 工具来检查数据的唯一性:
public class UniqueEmailAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var _context = (DatabaseContext)validationContext.GetService(typeof(DatabaseContext));
var entity = _context.Users.SingleOrDefault(e => e.Email == value.ToString());
if (entity != null)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
public string GetErrorMessage()
{
return "Email address must be unique";
}
}
public class Customer
{
[Required(ErrorMessage = "Email Address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
[UniqueEmail]
public string Email { get; set; }
}
数据注释的用例
电子商务平台为确保客户输入的数据准确无误(error-free),使用数据注释对模型进行输入验证:
public class Product
{
[Required]
[StringLength(100)]
public string Title { get; set; }
[Range(0.01, 1000)]
public decimal Price { get; set; }
}
数据注释作用于 Product
类,以确保 Title
不为空且最大长度为 100 字符,而 Price
则在指定范围内。
教育应用则会面对师生复杂交互的困扰,而数据注释可以满足这些要求(也不知道作者是咋想的),如下:
public class Student
{
[Required]
[StringLength(50)]
public string FirstName { get; set; }
[Required]
[StringLength(50)]
public string LastName { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime DateOfBirth { get; set; }
[Required]
[UniqueStudentID]
public int StudentID { get; set; }
}
在此模型中,数据注释再次发威以确保数据完整性。请注意它们是如何验证 FirstName
与 LastName
是否已提供并遵守设定的字符限制的;同时,DateOfBirth
必须是有效日期,且每个 StudentID
都应是唯一的。
数据注释 Data Annotations 超越平凡,使 C# 剑之所向,即非凡精度之所降。
比较
Fluent Validation 与 Data Annotations 间终有一战,以正合,以奇胜。
区别
流畅验证与数据注释的目的是相同的,但使用方式截然不同。
Code Organization
代码组织方面。流畅验证需要一个单独的类来进行验证,而数据注释内聚性更高,将验证属性直接置于类之中。以验证年龄为例:
使用 Fluent Validation,我们这样:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Age).GreaterThanOrEqualTo(18).WithMessage("Customer must be at least 18 years old");
}
}
使用 Data Annotations,我们那样:
public class Customer
{
[Required]
[Range(18, Int32.MaxValue, ErrorMessage = "Customer must be at least 18 years old")]
public int Age { get; set; }
}
由此可见两者在代码组织方面的差异。
Complexity Handling
复杂性处理方面。流畅验证多用于复杂场景和高级验证应用,而在其他相对简单的、不需要复杂的场景下,多用数据注释。
验证一个客户是否年满 18 岁且持有保险,使用 Fluent Validation,我们这样:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.HasInsurancePolicy).Equal(true);
RuleFor(customer => customer.Age).GreaterThanOrEqualTo(18);
}
}
虽然通过数据注释也可以做到相同的能力,但会更复杂一些。
Customizability
可定制性方面。两者在这一方面表现不同。Fluent Validation 在复杂验证和自定义方面更胜一筹,Data Annotations 稍逊风骚,但胜在门槛低。
下面是他们的差异化一览
标准 | 流畅验证 | 数据注释 |
---|---|---|
Code Organization | 使用单独的验证类 | 在类内实现 |
Complexity Handling | 适合复杂场景 | 适合简单的、基于属性的验证 |
Customizability | 高度可定制(提供自定义验证) | 低可定制(仅限于提供的属性) |
复杂性比较:易用性与灵活性
如古人所说,Regard your code as your castle. 我们必须决出胜负(译者表示中二无助于理解这段)。
流畅验证
沙场老兵,擅长处理疑难杂症:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Name).NotNull().WithMessage("Customer name cannot be null or empty");
RuleFor(customer => customer.HasInsurancePolicy).Equal(true).WithMessage("Customer must own an insurance policy");
RuleFor(customer => customer.Age).GreaterThanOrEqualTo(18).WithMessage("Customer must be at least 18 years old");
}
}
数据注释
骑士忠犬,贴身护卫。
比较它两的易用性和灵活性:
标准 | 流畅验证 | 数据注释 |
---|---|---|
使用方便 | 中等(功能需要理解多个类的复杂性) | 低(易于应用的内置属性) |
灵活性 | 高(允许创建复杂的验证规则) | 低(仅限于提供的内置属性) |
可定制性 | 高(用于自定义规则和消息的强大 API) | 低(有限的定制选项) |
代码可重用性 | 高(可重用验证类) | 低(注释与模型相关,因此可重用性较低) |
跨层适用性 | 高(与数据模型无关。可以在不同层使用) | 低(通常与数据模型相关) |
性能分析:孰快?
拼刺刀的时间又到了,Fluent Validation 和 Data Annotations 再次掏出了他们的小刺刀,孰快?
流畅验证虽灵活,但动作较数据注释稍慢,沙场角逐胜负转瞬即逝,鹿死谁手尚未定论!
而数据注释速度稍快,一寸短一寸险!但在应用中,这种性能提升可能很小,而且不会对大多数应用产生重大影响。
比较结果:
标准 | 流畅验证 | 数据注释 |
---|---|---|
表现 | 由于复杂的验证处理而速度较慢 | 由于简单的属性匹配,速度更快 |
应用场景 | 复杂、定制和多层验证的理想选择 | 适合简单的单层验证场景 |
可测试性 | 高(验证逻辑可以单独测试) | 中(涉及一起测试模型和验证规则) |
与 ORM 集成 | 较低(需要单独的验证调用) | 更高(默认与实体框架集成) |
牢记,速度不是胜负的关键,场景才是!对于复杂场景,交给专业的老兵;而简单场景下,信任你的忠犬!
实际应用与实践
开发人员通常倾向于实际操作的技术应用示例,光靠理论可能不足以解决很多问题。下面将深入应用场景来实际感受流畅验证与数据注释的异同之处。
使用流畅验证构建软件解决方案
你可能会对 Fluent Validation 在您每天使用的软件解决方案中的使用如此普遍感到惊讶:
电商平台
Fluent Validation 对于在线购物系统的顺利运行起着巨大的作用。考虑一个具有不同输入字段的购物车功能。我们以优惠券代码功能为例:
public class CouponValidator : AbstractValidator<Coupon>
{
public CouponValidator()
{
RuleFor(coupon => coupon.Code).NotEmpty().WithMessage("Coupon code is required.");
RuleFor(coupon => coupon.Discount).GreaterThan(0).WithMessage("Discount should be greater than zero.");
}
}
在此代码中,电子商务应用程序验证用户输入的优惠券代码不为空并且与其关联的折扣大于零。
金融系统
对于投资应用程序等金融系统,流畅的验证至关重要。用户为特定股票设置价格警报就是一个很好的例子。
public class PriceAlertValidator : AbstractValidator<PriceAlert>
{
public PriceAlertValidator()
{
RuleFor(alert => alert.Stock).NotNull().WithMessage("A stock is required for setting a price alert.");
RuleFor(alert => alert.ThresholdPrice).GreaterThan(0).WithMessage("The threshold price must be greater than zero.");
}
}
在此代码片段中,金融应用程序确保在设置价格警报时提及股票,并且警报价格大于零。
使用数据注释的成功案例
同样,数据注释已在各个领域得到应用:
CMS
假设您正在构建一个博客系统;确保博客文章 URL 唯一且正确至关重要。在这种情况下,您可以使用数据注释。
public class BlogPost
{
[Required, MaxLength(150)]
public string Title { get; set; }
[Required]
[RegularExpression(@"^(?:([\w\d\s-]+)=(?:http|https)://([\w\d-]+)\.([\w\d\.]+)/([\w\d-]+))$",
ErrorMessage = "Please enter a valid URL.")]
public string URL { get; set; }
}
在此代码中,数据注释验证创建新博客文章时输入的 URL 是否有效,以及提供的标题不为空或长度超过 150 个字符。
票务系统
在线预订机票时,正确填写所有字段非常重要。让我们考虑预订表单验证。
public class TicketBooking
{
[Required]
public string Name { get; set; }
[Required, RegularExpression(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")]
public string Email { get; set; }
[Required, Range(1, 10)]
public int Quantity { get; set; }
}
在这里,数据注释确保填写姓名和电子邮件字段,并且门票数量范围为 1 到 10。
因此,无论是流式验证还是数据注释,每种服务在现实世界中都具有实用价值,它们之间的选择通常取决于您的具体要求和偏好。
结案:哪个更好?
又来了,哪个更好?难以选择。
本节提供全面的比较,考虑项目复杂性、易用性、灵活性和性能等因素。目的是指导您做出明智的决定。
注意事项
需要考虑的关键方面包括:
项目复杂性
项目的规模和复杂性对于选择正确的验证框架至关重要。您的项目是大规模且具有复杂的验证要求,还是简单且不需要复杂的验证?
因素 | 流畅的验证 | 数据注释 |
---|---|---|
简单 | ❌ 需要 | ✅ 简单的基于属性的方法 |
复杂场景 | ✅ 支持复杂的验证场景 | ❌ 对高级验证要求的支持有限 |
易于使用和灵活性
考虑验证方法提供的学习曲线和灵活性。您是否更喜欢对验证规则进行精确控制,或者简单的解决方案适合您?
因素 | 流畅的验证 | 数据注释 |
---|---|---|
使用方便 | ❌ 更陡峭的学习曲线 | ✅ 更容易实施,涉及的编码更少 |
灵活性 | ✅ 高灵活性,自定义验证规则 | ❌ 灵活性较低,标准验证规则 |
性能影响
考虑对性能的影响。小延迟可能会成为一个重大问题,尤其是在大规模数据密集型项目中。
因素 | 流畅的验证 | 数据注释 |
---|---|---|
表现 | ✅ 通常表现更好 | ❌ 可能会在大数据应用程序中引入延迟 |
适用性
适用性:为你的项目选择合适的验证方案
了解您的项目的要求将指导您的决定:
-
对于不太复杂、基于属性的项目,数据注释可能更合适。
-
选择 Fluent Validation 来处理复杂的自定义规则。
回头看:到底哪个更好?
最终,选择并不明确。这在很大程度上取决于您项目的独特要求。
如果您有一个简单的项目,没有复杂的验证规则,那么实现数据注释的简便性和速度可能是您的最佳选择。
但是,如果您正在处理具有自定义验证规则或复杂需求的项目,那么 Fluent Validation 提供的灵活性和控制可能会非常有益。
请记住,最好的解决方案在满足特定项目需求、确保数据有效性和保持代码效率之间取得平衡。
总结
亲爱的读者(译者:你读到这,我也蛮佩服你的耐心的),接下来又到了选择的关键时刻。
了解验证需求的总体情况
套用简单公式: Project Requirements + Validation Needs = Best Validation Technique
。
最终想法:确保 C# 编码实践的质量与效率
请记住,各位,验证是您的防线,防止混乱的数据试图侵入您美丽的代码库!无论是流畅验证还是数据注释,请明智地选择您的技术并维护 C# 代码的神圣性。
各位 C# 的老司机,请以帅气的方式开着你们的突突车,愿流畅验证或数据注释与你们同在。
(老外的废话可真多……)
原文:https://www.bytehide.com/blog/fluent-validation-vs-data-annotations-csharp
作者:Juan España