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 没有意义的主要内容,如果未能解决你的问题,请参考以下文章

Swagger 需要所有属性

LLDB – 在任意对象的实例变量上设置观察点

在过滤器中访问 request.JSON 后 Grails 2.5.0 控制器命令对象绑定

java中访问控制权限修饰符

为啥对 ms 访问数据库的只读访问的最低权限没有提及对 .laccdb(锁定文件)的任何权限要求?

ECharts——运行错误[TypeError: Cannot read property ‘type‘ of undefined]解决方案