业务对象属性验证






3.43/5 (9投票s)
如何在一行代码中设置和验证属性。
引言
大家好。我目前正在开发一个内部使用的n层应用程序,并通过反射减少了大量的代码。唯一仍然需要手动、繁琐编码的部分是客户端业务对象。对于较小的对象,这不算什么大问题,但其中一些对象有100多个公共读/写属性——当然,还有存储数据的私有字段声明。我真正反对的是构建属性和包含一些基本的验证和错误检查的重复性工作。
使用代码
为了方便阅读,示例已经简化,但可以根据您的需求进行扩展并移至不同的层。我包含了一个BusinessRules程序集,其中包含许多自定义属性。这些属性进一步简化了验证过程,但属性是另一篇文章的主题。
属性 - Structures.vb
我们将从一个structTEST
结构开始。它将作为业务对象的变量和属性,并将被验证。
Option Strict Off
Option Explicit On
Imports BusinessRules.Attributes
Namespace Structures
Public Structure structTEST
'Use the custom attributes defined in the BusinessRules.Attributes
'namespace. These attributes will form part of the general/generic
'business rules that apply to this property
<NotNull(), NotEmpty(), MaxLength(50)> Public Name As String
End Structure
End Namespace
注意:我总是将我的结构放在Structures
命名空间中,以保持应用程序的有序和逻辑。这些结构通常保存在服务器端业务对象和/或DAL接口声明的同一个模块中。它们可以放在一个Structures项目,或者全部存储在一个Structure代码模块中。选择权在于您。
验证器 - CProps.vb
这个类做了所有工作……它将执行属性的Get/Set
,执行验证,并抛出任何错误/异常。
Option Strict Off
Option Explicit On
Imports PropsVal.Structures
Imports BusinessRules.Attributes
Imports BusinessRules.Errors
Imports System.Reflection
在这里,我们设置了强制性内容并导入了相应的命名空间。请注意,我们导入了BusinessRules.Attributes
和BusinessRules.Errors
命名空间以及System.Reflection
。
Public Class CProps
#Region " Private Attributes "
Private mVTProps As ValueType
Private lType As Type
#End Region
现在,我们定义类名并开始声明一些模块变量。请注意,mVTProps
对象被声明为ValueType
。
#Region " Private Properties "
Private ReadOnly Property VTType() As Type
Get
Try
If lType Is Nothing Then
lType = mVTProps.GetType
End If
Return lType
Catch ex As Exception
Throw ex
End Try
End Get
End Property
#End Region
现在,我们定义一个稍后将使用的私有属性。此属性的作用是向内部方法返回一个System.Type
,因为它们需要它(如下所示)。
#Region " Constructors "
Public Sub New(ByVal typeName As String)
Try
Dim t As Type = Type.GetType(typeName)
If t Is Nothing Then
Throw New Exception("Type could not be loaded.")
Return
End If
mVTProps = Activator.CreateInstance(t)
Catch ex As Exception
Throw ex
End Try
End Sub
#End Region
继续……定义了构造函数。在这里,我们使用typeName
(String
)参数来初始化模块变量(上面)。
#Region " Public Methods "
Public Sub SetValue(ByVal propName As String, ByVal val As Object)
Try
Dim lObjFI As FieldInfo = VTType.GetField(propName)
lObjFI.SetValue(mVTProps, val)
BusinessRules.Validate.Validation.ValidateAndThrow(mVTProps, propName)
Catch ex As Exception
Throw ex
End Try
End Sub
然后,进入工作人员。与所有读/写属性一样,都有一个Set
块。这是Set
块。现在,还记得上面我们将mVTProps
变量声明为ValueType
吗?这是因为结构是值类型。FieldInfo.SetValue
只能作用于Object
类型(任何熟悉此问题的人都可以随意阐述)。
Public Function GetValue(ByVal propName As String)
Try
Dim lObjFI As FieldInfo = VTType.GetField(propName)
Return lObjFI.GetValue(mVTProps)
Catch ex As Exception
Throw ex
End Try
End Function
Public Function GetAllData() As ValueType
Return mVTProps
End Function
#End Region
End Class
……这是Get
块和类的结尾。
业务对象
业务对象是用户界面将与之通信的对象。
Option Strict Off
Option Explicit On
Imports PropsVal.Structures
Imports System.Reflection
Public Class BizO
#Region " Public Properties "
Public PROPS As CProps
#End Region
再次,我们设置选项并导入命名空间;定义类和公共属性。PROPS
声明是您通常声明所有变量(string、int、GUID等)的地方,然后您将拥有所有属性声明以及任何/所有验证和错误检查代码。
#Region " Constructors "
Public Sub New()
'Initialise the PROPS object
PROPS = New CProps(GetType(Structures.structTEST).FullName)
End Sub
#End Region
#Region " Public Methods "
Public Sub Load()
'This is where you would make calls to your database,
'DAL, or remote objects. This would return the data
'and fill the PROPS object (above)
'This model can then be expanded upon by providing similar
'method - ie Save, Rollback, etc...
End Sub
#End Region
End Class
构造函数是初始化PROPS
对象的地方。这是此解决方案的关键要素,因为类型必须正确,但即使这样也不算太难。还记得我们之前创建的结构吗?这就是我们实际使用它的地方。使这一切生效的关键是解析结构的Type FullName
。
在这里,我特意将Load
方法留空,但根据代码注释,您可以看到可以做什么。
UI - form1.vb
是的,是的。我知道名字很随意。您需要添加的唯一代码是关于按钮的代码。
'Declare and instantiate the BizO business object
Dim o As BizO
o = New BizO
Try
'Set the value of the Name property in the business object
o.PROPS.SetValue("Name", txtName.Text)
'No exceptions/errors were detected during the value set
'so report the success to the user
lblErrMsg.Text = "Input tested successfully!!"
pnlStatus.BackColor = Color.Green
Catch ex As Exception
'An exception was thrown from within the PROPS object (inside
'the business object
'Report the error message to the user
lblErrMsg.Text = ex.Message
pnlStatus.BackColor = Color.Red
End Try
'Display the results to the user
lblResult.Text = "PROPS.GetValue: " + o.PROPS.GetValue("Name")
'Declare a local instance of structTEST structure
Dim x As Structures.structTEST
'and fill it with ALL the data from the PROPS object
x = o.PROPS.GetAllData
'Display the results to the user
lblResult.Text += vbCrLf + vbCrLf + "Local structure: " + x.Name
好的,最后一部分……在这里,我们声明并实例化一个新的业务对象。
然后,我们尝试将Name
属性设置为文本框Text
属性的值。如果没有错误/异常,我们将显示成功消息并将面板设置为绿色。任何异常都会导致显示错误消息并将面板更改为红色。
然后,纯粹是为了测试目的,在我构建此内容时,我们检索Name
属性的值并将其显示在屏幕上,最后我们声明structTEST
结构的新实例并用PROPS
对象的内容/值填充它,并显示Name
成员的值。
关注点
- 上述代码
是最初在v1.1框架上开发的。由于我还没有使用v2.0,我只能假设上述内容会起作用,但请不要引用我的话。在将其移植到VS2005(Framework 2.0)解决方案然后是VS2008之后,没有出现任何问题,并且该模型正在使用中,而且每天都在被更多地使用。 structTEST
结构是值类型,使用FieldInfo.SetValue
只能作用于对象类型。幸运的是,结构很容易转换为值类型对象。Option Strict
设置为Off
,仅仅是因为将结构转换为ValueType
对象而不进行CType
会失败,而且我发现当严格类型设置为开启时,有太多其他隐式类型转换会失败。GetAllData
方法可用于将内容/值解析到数据库、DAL、或远程对象,因为返回的对象是structTEST
结构。我正在开发的应用程序将这些结构解析到一个可远程化的业务对象(通过接口),在那里进行最终的检查和验证。然后,结构被解析到DAL,其中结构用于填充存储过程参数(使用成员名称和进一步的自定义属性),并将其解析到SQL数据库。- 我本想利用IntelliSense来获取属性名称,而不是使用
String
字面量,但我担心这会增加额外的开销,从而抵消任何性能和/或简洁性。有什么想法吗? - 我确信还有很多可以做得更好的地方,我非常乐意听到您的意见。毕竟,我们都在这里学习。
结论
如今,每个人都必须在更少的帮助下做更多的事情,而且这总是在昨天就该完成。希望这个例子能帮助您减少乏味的工作,让您能继续做真正酷的事情。如果我再次受到启发,我可能会提交一些关于我发现、创建和使用的各种技术以节省时间和代码的进一步文章。