空处理
每一个数据存取系统都有一个特殊的构造来处理那些没有明确指定的字段值。在大多数关系数据库管理系统中,这个构造就是众所周知的null值。
从应用程序的角度看,在表述层和数据存取层传递null值是一个架构上的挑战。这是因为表述层必须从数据库的特定信息抽象出来;而且,当一个属性值没有明确指定的时候表述层也必须能够表达说明。
事实上这相当复杂,.NET Framework的本身的数据类型不能自动的转换从数据库返回的null值(如果你试图直接那样赋值的话将会抛出一个异常)。另外,每一个数据存储都有它自己的属性来实现null。
唯一合理的解决方案就是创建一个抽象的传输服务,来编码/解码应用程序各层之间的null值。
乍一看,你也许会想到用vb.net中的“nothing”关键字可以很好的担负起这个传输服务的任务。不幸的是,调查显示,.NET Framework本身的数据类型处理“nothing”的时候没有预想的那么好。尽管分配为nothing的属性不会抛出异常,实际上这个属性的值将非常依赖于它的数据类型(String = Nothing, Date = Date.MinValue, Integer = 0, Boolean = False, 等等)并且自带的IsNothing()函数的结果还不是一致的(兼容的)结果。
在DNN里,我们创建了一个通用的类来处理null的问题,它统一管理应用程序各层的null问题。在应用程序中用一个常量来描述每种数据类型的null情况,再把这个常量转化成各种数据存储实现里的实际的null值。这个类包含的各种方法将null转换服务的物理细节从应用程序中抽象出来了。
* 记住,这个类仅仅用在数据库字段允许有null值的情况下。还要记住,这个类要求DAL和BLL层之间的数据类型一致(例如:一个BLL信息类里的属性字段的数据类型必须跟DAL 数据提供者传递过来的参数的数据类型一致)。
Public Class Null
' define application encoded null values
Public Shared ReadOnly Property NullInteger() As Integer
Get
Return -1
End Get
End Property
Public Shared ReadOnly Property NullDate() As Date
Get
Return Date.MinValue
End Get
End Property
Public Shared ReadOnly Property NullString() As String
Get
Return ""
End Get
End Property
Public Shared ReadOnly Property NullBoolean() As Boolean
Get
Return False
End Get
End Property
' sets a field to an application encoded null value ( used in Presentation layer )
Public Shared Function SetNull(ByVal objField As Object) As Object
If TypeOf objField Is Integer Then
SetNull = NullInteger
ElseIf TypeOf objField Is Date Then
SetNull = NullDate
ElseIf TypeOf objField Is String Then
SetNull = NullString
ElseIf TypeOf objField Is Boolean Then
SetNull = NullBoolean
Else
Throw New NullReferenceException()
End If
End Function
' sets a field to an application encoded null value ( used in BLL layer )
Public Shared Function SetNull(ByVal objPropertyInfo As PropertyInfo) As Object
Select Case objPropertyInfo.PropertyType.ToString
Case "System.Int16", "System.Int32", "System.Int64", "System.Single", "System.Double", "System.Decimal"
SetNull = NullInteger
Case "System.DateTime"
SetNull = NullDate
Case "System.String", "System.Char"
SetNull = NullString
Case "System.Boolean"
SetNull = NullBoolean
Case Else
Throw New NullReferenceException()
End Select
End Function
' convert an application encoded null value to a database null value ( used in DAL )
Public Shared Function GetNull(ByVal objField As Object, ByVal objDBNull As Object) As Object
GetNull = objField
If TypeOf objField Is Integer Then
If objField = NullInteger Then
GetNull = objDBNull
End If
ElseIf TypeOf objField Is Date Then
If objField = NullDate Then
GetNull = objDBNull
End If
ElseIf TypeOf objField Is String Then
If objField = NullString Then
GetNull = objDBNull
End If
ElseIf TypeOf objField Is Boolean Then
If objField = NullBoolean Then
GetNull = objDBNull
End If
Else
Throw New NullReferenceException()
End If
End Function
' checks if a field contains an application encoded null value
Public Shared Function IsNull(ByVal objField As Object) As Boolean
If objField = SetNull(objField) Then
IsNull = True
Else
IsNull = False
End If
End Function
End Class
项目实际使用DA层
SqlDataProvider (具体实现类)
在具体类中包含了下面的帮助方法,这个方法用来独立数据库null的实现(这个例子中DBNull.Value 是针对SQL Server而言的)并且提供一个简单的接口。
' general
Private Function GetNull(ByVal Field As Object) As Object
Return Null.GetNull(Field, DBNull.Value)
End Function
每一个在基类里表明必须继承的方法在具体类里都必须实现。注意上面的add/update方法里描述的GetNull()函数的使用。
' announcements module
Public Overrides Function GetAnnouncements(ByVal ModuleId As Integer) As IDataReader
Return CType(SqlHelper.ExecuteReader(ConnectionString, DatabaseOwner & ObjectQualifier & "GetAnnouncements", ModuleId), IDataReader)
End Function
Public Overrides Function GetAnnouncement(ByVal ItemId As Integer, ByVal ModuleId As Integer) As IDataReader
Return CType(SqlHelper.ExecuteReader(ConnectionString, DatabaseOwner & ObjectQualifier & "GetAnnouncement", ItemId, ModuleId), IDataReader)
End Function
Public Overrides Sub DeleteAnnouncement(ByVal ItemId As Integer)
SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "DeleteAnnouncement", ItemId)
End Sub
Public Overrides Sub AddAnnouncement(ByVal ModuleId As Integer, ByVal UserName As String, ByVal Title As String, ByVal URL As String, ByVal Syndicate As Boolean, ByVal ExpireDate As Date, ByVal Description As String, ByVal ViewOrder As Integer)
SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "AddAnnouncement", ModuleId, UserName, Title, URL, Syndicate, GetNull(ExpireDate), Description, GetNull(ViewOrder))
End Sub
Public Overrides Sub UpdateAnnouncement(ByVal ItemId As Integer, ByVal UserName As String, ByVal Title As String, ByVal URL As String, ByVal Syndicate As Boolean, ByVal ExpireDate As Date, ByVal Description As String, ByVal ViewOrder As Integer)
SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "UpdateAnnouncement", ItemId, UserName, Title, URL, Syndicate, GetNull(ExpireDate), Description, GetNull(ViewOrder))
End Sub