MS 访问控制 Property.Type 没有意义
Posted
技术标签:
【中文标题】MS 访问控制 Property.Type 没有意义【英文标题】:MS Access Control Property.Type not making sense 【发布时间】:2015-02-25 05:51:51 【问题描述】:我过去曾编写过处理 TableDef 和 Field 属性的代码。这并不难,它只涉及遍历 Field.Properties 集合并在此过程中进行一定数量的错误检查。 Property.Name、Property.Type 和 Property.Value 产生了我们需要的一切。
问题:我刚刚编写了一些代码来对 Form 和 Report 控件进行相同类型的属性枚举。属性名称和值很好,但 .Type 不正确。一个例子:
Public Sub TestPropTypes()
Dim dbs As DAO.Database
Set dbs = CurrentDb()
Dim td As TableDef
Dim fld As DAO.Field
Set td = dbs.TableDefs("CalendarEvent_")
Set fld = td.Fields("EventDescription")
PrintObjectProps fld
Dim f As Form
Dim c As Control
DoCmd.OpenForm "fmCalendarDates", acDesign
Set f = Forms!fmCalendarDates
Set c = f.Controls("Label7")
PrintObjectProps c
End Sub
Private Sub PrintObjectProps(c As Object, Optional RecursionDepth As Integer = 0)
Dim ExistingProperty As DAO.Property
Dim PropCount As Integer
Dim GotValue As Boolean
Dim v As Variant
Debug.Print c.Name
For Each ExistingProperty In c.Properties
If PropCount > 12 Then Exit Sub
GotValue = True
On Error Resume Next
v = ExistingProperty.Value
If Err.number <> 0 Then GotValue = False
On Error GoTo 0
If GotValue Then
Debug.Print " " & ExistingProperty.Name & " " _
& GetFieldDDLTypeName(ExistingProperty.Type) & "(" & ExistingProperty.Type & ") " _
& xf.Gen.dq(CStr(ExistingProperty.Value))
End If
PropCount = PropCount + 1
Next
End Sub
Public Function GetFieldDDLTypeName(FieldType As DAO.DataTypeEnum) As String
Dim rtnStr As String
' as per: http://allenbrowne.com/ser-49.html
Select Case FieldType
Case dbBoolean: rtnStr = "YESNO"
Case dbByte: rtnStr = "BYTE"
Case dbInteger: rtnStr = "SHORT"
Case dbLong: rtnStr = "LONG"
Case dbCurrency: rtnStr = "CURRENCY"
Case dbSingle: rtnStr = "SINGLE"
Case dbDouble: rtnStr = "DOUBLE"
Case dbDate: rtnStr = "DATETIME"
Case dbBinary: rtnStr = "BINARY"
Case dbText: rtnStr = "TEXT"
Case dbLongBinary: rtnStr = "LONGBINARY"
Case dbMemo: rtnStr = "MEMO"
Case DBGuid: rtnStr = "GUID"
End Select
GetFieldDDLTypeName = rtnStr
End Function
产量:
TestPropTypes
EventDescription
Attributes LONG(4) "2"
CollatingOrder SHORT(3) "1033"
Type SHORT(3) "10"
Name MEMO(12) "EventDescription"
OrdinalPosition SHORT(3) "2"
Size LONG(4) "100"
SourceField MEMO(12) "EventDescription"
SourceTable MEMO(12) "CalendarEvent_"
DataUpdatable YESNO(1) "False"
DefaultValue MEMO(12) ""
Label7
EventProcPrefix DATETIME(8) "Label7"
Name DATETIME(8) "Label7"
ControlType BYTE(2) "100"
Caption DATETIME(8) "Description"
Visible LONGBINARY(11) "True"
Width BYTE(2) "1875"
Height BYTE(2) "285"
Top BYTE(2) "425"
Left BYTE(2) "1048"
BackStyle BYTE(2) "0"
BackColor SHORT(3) "16777215"
BorderStyle BYTE(2) "0"
OldBorderStyle BYTE(2) "0"
因此,您可以看到第一组,来自 TableDef 中的字段,产生了我们期望的结果。
第二组,使用完全相同的呈现代码,来自窗体上的控件。所有的 MEMO 属性都以 DATETIME 的形式出现,所有的 YESNO 都以 LONGBINARY 的形式出现,等等。它至少是一致的,我可能可以推断出类型转换并编写一个转换算法。一开始我以为它可能使用了 ADO 值(因为 YESNO 是 11),但是与其他 ADO 值没有一致性,而且我们还明确使用了 DAO 对象,所以这没有意义。
这是不一致的,我想制定并记录某种合理的解决方案。 有没有人遇到过这种情况?
在 Access 2003 和 2007 上测试,所以我确信这不是我的 Access 版本中的一个奇怪错误。这是一个 Access 2003 格式的数据库,因此没有扩展类型在工作中使用扳手。
编辑:由于 HansUp,问题得以解决。现在我们正在检查表/字段与表单/报表属性是否使用不同的返回值。我提供这个:
Public Sub TestPropTypes1()
Dim prop As DAO.Property
Dim dbs As DAO.Database
Set dbs = CurrentDb()
DoCmd.OpenForm "fmCalendarDates", acDesign
Debug.Print "Property", "Value", "varType(.Value)", ".Type"
Debug.Print
Set prop = dbs.TableDefs("CalendarEvent_").Fields("EventDate").Properties("Name")
Debug.Print prop.Name, prop.Value, varType(prop.Value), prop.Type
Set prop = dbs.TableDefs("CalendarEvent_").Fields("EventDate").Properties("Required")
Debug.Print prop.Name, prop.Value, varType(prop.Value), prop.Type
Set prop = Forms!fmCalendarDates!Label7.Properties("Name")
Debug.Print prop.Name, prop.Value, varType(prop.Value), prop.Type
Set prop = Forms!fmCalendarDates!Label7.Properties("Visible")
Debug.Print prop.Name, prop.Value, varType(prop.Value), prop.Type
End Sub
屈服
TestPropTypes1
Property Value vartype(prop.Value) prop.Type
Name EventDate 8 12
Required False 11 1
Name Label7 8 8
Visible True 11 11
这强烈表明 TableDef 字段属性确实使用 DAO.DataTypeEnum 类型,而表单属性似乎会产生 VBA.VbVarType 返回。 例如,该字段的Required属性返回一个对应于NULL的VbVarType的类型,而它是一个YesNo的DataTypeEnum。
注意 prop.Type 和 varType(prop.Value) 之间的细微差别
虽然我们可以使用 varType(prop.value),但这通常被认为是不好的做法,因为类型可能取决于值的内容(例如 Null),而 .Type 是权威元数据。在像这样的系统属性的情况下,这些值可能表现良好,并且可能没有实际差异。
真正令人惊讶的是,参考资料根本没有提到这个问题。
【问题讨论】:
【参考方案1】:在评估属性 Type
时使用 VBA.VbVarType
枚举。
您的代码将属性的Type
视为DAO.DataTypeEnum
的成员。这会导致代码错误地翻译Type
。
实际上这不是只出现在表单和报表控件中的问题。您的表的字段属性也有同样的问题。例如,输出示例错误地将字段的 Name
属性识别为备忘录类型:
Name MEMO(12) "EventDescription"
但是一个字段的Name
属性是一个变体,它的子类型是字符串。
? CurrentDb.TableDefs("tblFoo").Fields("long_text").Properties("Name").Type
12
? VBA.VbVarType.vbVariant
12
' this is the WRONG translation ...
? DAO.DataTypeEnum.dbMemo
12
如果您的目标是将属性的 Type
翻译成易于理解的文本,请考虑使用 TypeName()
函数:
? TypeName(CurrentDb.TableDefs("tblFoo").Fields("long_text").Properties("Name").Value)
String
如果该建议不可接受,您可以创建一个自定义函数将Type
翻译成您想要的文本。但是,将Type
转换为 DDL 字段数据类型名称是错误的方法,IMO。
【讨论】:
非常感谢,确实如此!不过,我不同意你答案的最后一部分。我已经对其进行了一些测试,我将根据测试结果编辑问题。 请注意,TypeName(....Value)
将返回当前属性值的 dynamic 类型,而不是属性本身的 static 类型。换句话说:如果属性是一个 Variant 并且它的当前值是一个字符串,TypeName 将返回String
,而不是Variant
。
TypeName
实际上只是varType
的变体。我在上面提出了同样的观点,但你做得更清楚了!【参考方案2】:
感谢您提供所有这些好信息。 我正在研究这个主题。在 Access2019 上工作了几个小时后,我可以添加一些提示。 我把GetFieldDDLTypeName()函数改成了后面的GetFieldVBATypeName(),解决了TypeNames不一致的问题,也解决了复合Array Types的问题。
虽然应用于 control.properties,但它显示出一些不一致:
Control.Property Type TypeName Value
1-OLEBound.VarOleObject 6 Currency 65
2-ToggleButton.InSelection 11 Boolean NULL
3-Image.InSelection 11 Boolean 6375
Public Function GetVBATypeName(FieldType As VBA.VbVarType) As String
Dim rtnStr As String
If FieldType > 8192 Then
rtnStr = GetVBATypeName2(8192) & "-" & GetVBATypeName2(FieldType - 8192)
Else
rtnStr = GetVBATypeName2(FieldType)
End If
GetVBATypeName = rtnStr
End Function
Public Function GetVBATypeName2(FieldType As VBA.VbVarType) As String
Dim rtnStr As String
Select Case FieldType
Case vbEmpty: rtnStr = "EMPTY"
Case vbNull: rtnStr = "NULL"
Case vbInteger: rtnStr = "INTEGER"
Case vbLong: rtnStr = "LONG"
Case vbSingle: rtnStr = "SINGLE"
Case vbDouble: rtnStr = "DOUBLE"
Case vbCurrency: rtnStr = "CURRENCY"
Case vbDate: rtnStr = "DATE"
Case vbString: rtnStr = "STRING"
Case vbObject: rtnStr = "OBJECT"
Case vbError: rtnStr = "ERROR"
Case vbBoolean: rtnStr = "BOOLEAN"
Case vbVariant: rtnStr = "VARIANT"
Case vbDataObject: rtnStr = "DATAOBJECT"
Case vbDecimal: rtnStr = "DECIMAL"
Case vbByte: rtnStr = "BYTE"
Case 20: rtnStr = "LONGLONG" 'vbLongLong ...
Case vbUserDefinedType: rtnStr = "USERDEFINED"
Case vbArray: rtnStr = "ARRAY"
Case Else: rtnStr = "Error DataType"
End Select
GetVBATypeName2 = rtnStr
End Function
注意案例20,对应vbLongLong,但不被VBA识别 ' 案例 vbLongLong: rtnStr = "LONGLONG" '(=20) 这里描述: https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/vartype-function#return-values。 如果您有任何更正,请在此处发布以贡献更好的软件。
【讨论】:
这是技术上的答案以上是关于MS 访问控制 Property.Type 没有意义的主要内容,如果未能解决你的问题,请参考以下文章
在过滤器中访问 request.JSON 后 Grails 2.5.0 控制器命令对象绑定
为啥对 ms 访问数据库的只读访问的最低权限没有提及对 .laccdb(锁定文件)的任何权限要求?
ECharts——运行错误[TypeError: Cannot read property ‘type‘ of undefined]解决方案