Visual Basic数据注解与特性验证
2024-09-293.7k 阅读
Visual Basic 数据注解概述
在 Visual Basic 开发中,数据注解提供了一种强大的机制,用于向代码元素(如类、属性、方法等)添加元数据。这些元数据并非代码逻辑的一部分,但它们能在运行时被反射机制读取,从而实现各种特定功能。例如,数据注解可用于数据验证、序列化控制、权限管理等。
数据注解本质上是一种特殊的特性(Attribute)。在 Visual Basic 中,特性是从 System.Attribute
类派生的类,通过在代码元素前使用方括号 []
语法来应用。例如,以下是一个简单的特性应用示例:
<Serializable>
Public Class MyClass
'类的成员
End Class
这里的 <Serializable>
就是一个特性,它标记 MyClass
类可被序列化。
内置的数据注解特性
RequiredAttribute
:此特性用于标记某个属性为必填项。在数据验证场景中,当验证带有RequiredAttribute
的属性时,如果该属性值为空(对于引用类型)或其默认值(对于值类型),验证将失败。
Imports System.ComponentModel.DataAnnotations
Public Class User
<Required(ErrorMessage:="用户名不能为空")>
Public Property Username As String
<Required(ErrorMessage:="密码不能为空")>
Public Property Password As String
End Class
StringLengthAttribute
:用于控制字符串类型属性的长度。可以指定最大长度、最小长度或两者都指定。
Public Class Product
<StringLength(50, MinimumLength:=2, ErrorMessage:="产品名称长度必须在 2 到 50 个字符之间")>
Public Property ProductName As String
End Class
RangeAttribute
:适用于数值类型属性,用于限定属性值的范围。
Public Class Order
<Range(1, 1000, ErrorMessage:="订单数量必须在 1 到 1000 之间")>
Public Property Quantity As Integer
End Class
RegularExpressionAttribute
:通过正则表达式来验证字符串属性的值。
Public Class EmailModel
<RegularExpression("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", ErrorMessage:="请输入有效的电子邮件地址")>
Public Property Email As String
End Class
特性验证的执行
在 Visual Basic 中,特性验证通常借助 DataAnnotationsValidator
类或相关的验证框架来执行。以 ASP.NET MVC 为例,模型绑定过程中会自动触发特性验证。
- 手动验证:可以手动使用
Validator
类来验证对象。
Imports System.ComponentModel.DataAnnotations
Public Class Program
Public Shared Sub Main()
Dim user As New User()
user.Username = ""
user.Password = ""
Dim validationResults As New List(Of ValidationResult)()
Dim isValid = Validator.TryValidateObject(user, New ValidationContext(user), validationResults, True)
If Not isValid Then
For Each result In validationResults
Console.WriteLine(result.ErrorMessage)
Next
End If
End Class
End Program
- ASP.NET MVC 中的验证:在 ASP.NET MVC 项目中,视图模型的特性验证会在模型绑定到控制器操作方法参数时自动执行。
Imports System.Web.Mvc
Public Class AccountController
Inherits Controller
<HttpPost>
Public Function Register(user As User)
If ModelState.IsValid Then
'保存用户逻辑
Return RedirectToAction("Index", "Home")
Else
Return View(user)
End If
End Function
End Class
在视图中,可以通过 Html.ValidationMessageFor
等辅助方法来显示验证错误信息。
@model User
@Using(Html.BeginForm())
@Html.LabelFor(Function(m) m.Username)
@Html.TextBoxFor(Function(m) m.Username)
@Html.ValidationMessageFor(Function(m) m.Username)
@Html.LabelFor(Function(m) m.Password)
@Html.PasswordFor(Function(m) m.Password)
@Html.ValidationMessageFor(Function(m) m.Password)
<input type="submit" value="注册" />
End Using
创建自定义数据注解特性
虽然内置的数据注解特性能满足大部分常见需求,但在某些特定业务场景下,可能需要创建自定义特性。
- 创建自定义特性类:自定义特性类必须从
System.Attribute
类派生。
Imports System.ComponentModel.DataAnnotations
Public Class CustomEmailDomainAttribute
Inherits ValidationAttribute
Private ReadOnly allowedDomain As String
Public Sub New(ByVal domain As String)
allowedDomain = domain
End Sub
Public Overrides Function IsValid(ByVal value As Object) As Boolean
If value Is Nothing Then
Return True
End If
Dim email As String = CStr(value)
Dim parts = email.Split("@")
If parts.Length < 2 Then
Return False
End If
Return parts(1).EndsWith(allowedDomain)
End Function
End Class
- 应用自定义特性:
Public Class CompanyEmailModel
<CustomEmailDomain("example.com", ErrorMessage:="必须使用公司邮箱地址")>
Public Property Email As String
End Class
- 验证自定义特性:验证方式与内置特性类似,可以手动验证或在框架中集成验证。
Imports System.ComponentModel.DataAnnotations
Public Class Program
Public Shared Sub Main()
Dim model As New CompanyEmailModel()
model.Email = "test@otherdomain.com"
Dim validationResults As New List(Of ValidationResult)()
Dim isValid = Validator.TryValidateObject(model, New ValidationContext(model), validationResults, True)
If Not isValid Then
For Each result In validationResults
Console.WriteLine(result.ErrorMessage)
Next
End If
End Class
End Program
数据注解在不同框架中的应用
- ASP.NET Web Forms:在 ASP.NET Web Forms 中,可以使用
Validator
控件结合数据注解来实现验证。例如,RegularExpressionValidator
控件可与RegularExpressionAttribute
配合使用。
<asp:TextBox ID="txtEmail" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator ID="revEmail" runat="server"
ControlToValidate="txtEmail"
ValidationExpression="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
ErrorMessage="请输入有效的电子邮件地址"></asp:RegularExpressionValidator>
- Windows Forms:在 Windows Forms 应用程序中,可以在代码后端手动执行数据注解验证。例如,在按钮点击事件中验证文本框输入。
Imports System.ComponentModel.DataAnnotations
Public Class MainForm
Inherits Form
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim user As New User()
user.Username = txtUsername.Text
user.Password = txtPassword.Text
Dim validationResults As New List(Of ValidationResult)()
Dim isValid = Validator.TryValidateObject(user, New ValidationContext(user), validationResults, True)
If Not isValid Then
For Each result In validationResults
MessageBox.Show(result.ErrorMessage)
Next
End If
End Sub
End Class
- Entity Framework:在 Entity Framework 中,数据注解可用于配置实体的映射和验证。例如,
RequiredAttribute
可确保数据库表中对应的列不能为空。
Imports System.Data.Entity
Public Class MyDbContext
Inherits DbContext
Public Property Users As DbSet(Of User)
End Class
Public Class User
<Key>
Public Property UserId As Integer
<Required>
Public Property Username As String
<Required>
Public Property Password As String
End Class
数据注解与特性验证的性能考虑
- 反射开销:由于数据注解依赖反射来读取特性信息,频繁的反射操作会带来一定的性能开销。特别是在循环或高频率调用的代码段中,这种开销可能会变得显著。为了减轻反射开销,可以考虑缓存特性信息。例如,在应用程序启动时,通过反射一次性获取所有需要验证的类型及其特性,并存储在内存中,后续验证直接从缓存中读取。
Imports System.Collections.Concurrent
Imports System.Reflection
Public Class ValidationCache
Private Shared cache As New ConcurrentDictionary(Of Type, List(Of ValidationAttribute))
Public Shared Function GetAttributes(ByVal type As Type) As List(Of ValidationAttribute)
If Not cache.ContainsKey(type) Then
Dim attributes = type.GetProperties() _
.SelectMany(Function(p) p.GetCustomAttributes(Of ValidationAttribute)()) _
.ToList()
cache.TryAdd(type, attributes)
End If
Return cache(type)
End Function
End Class
- 验证逻辑复杂度:复杂的自定义验证逻辑,如涉及大量计算或外部资源访问的验证,会降低验证性能。应尽量保持验证逻辑简洁,将复杂计算移到验证之外的逻辑中。例如,如果验证某个属性需要查询数据库,可考虑在验证前先缓存相关数据,避免每次验证都进行数据库查询。
- 批量验证:在处理大量对象的验证时,采用批量验证方式可以减少性能开销。例如,在验证一个用户集合时,不是逐个用户进行验证,而是一次性对整个集合进行验证。可以通过扩展方法实现批量验证。
Imports System.Collections.Generic
Imports System.ComponentModel.DataAnnotations
<Extension()>
Public Module ValidationExtensions
<Extension()>
Public Function TryValidateAll(Of T)(ByVal entities As IEnumerable(Of T), ByRef validationResults As List(Of ValidationResult)) As Boolean
Dim isValid = True
For Each entity In entities
Dim context As New ValidationContext(entity)
Dim results As New List(Of ValidationResult)()
Dim entityIsValid = Validator.TryValidateObject(entity, context, results, True)
If Not entityIsValid Then
isValid = False
validationResults.AddRange(results)
End If
Next
Return isValid
End Function
End Module
数据注解与特性验证的常见问题及解决
- 特性冲突:当多个特性对同一属性有相互冲突的要求时,可能导致验证混乱。例如,
RequiredAttribute
和允许空值的其他特性同时应用于一个属性。解决方法是仔细设计特性组合,确保逻辑一致性。如果确实需要复杂的逻辑,可以通过自定义特性来整合相关验证逻辑。 - 本地化问题:默认情况下,数据注解的错误信息是硬编码的。在多语言应用程序中,需要对错误信息进行本地化。可以通过资源文件来实现。首先,创建一个资源文件(如
ValidationResources.resx
),然后在特性中指定资源键。
Imports System.ComponentModel.DataAnnotations
Public Class User
<Required(ErrorMessageResourceType:=GetType(ValidationResources), ErrorMessageResourceName:="UsernameRequired")>
Public Property Username As String
End Class
- 继承与特性覆盖:在继承体系中,子类可能需要覆盖或扩展父类属性上的特性。在 Visual Basic 中,可以在子类属性上重新应用特性来覆盖父类的设置。例如:
Public Class BaseClass
<StringLength(10)>
Public Property BaseProperty As String
End Class
Public Class DerivedClass
Inherits BaseClass
<StringLength(20)>
Public Overrides Property BaseProperty As String
End Class
通过深入理解 Visual Basic 数据注解与特性验证,开发者能够有效地提高代码的健壮性和可维护性,确保数据的完整性和准确性,同时根据不同的应用场景灵活定制验证逻辑。无论是简单的表单验证,还是复杂的企业级应用中的数据处理,数据注解与特性验证都能发挥重要作用。