-
Notifications
You must be signed in to change notification settings - Fork 4
/
MetaDescriptor.vb
304 lines (290 loc) · 14.7 KB
/
MetaDescriptor.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
Imports System.Reflection
Imports System.Runtime.CompilerServices
Imports System.Threading
Imports Databasic
Public Class MetaDescriptor
Private Shared _lock As ReaderWriterLockSlim = New ReaderWriterLockSlim()
Private Shared _register As New Dictionary(Of String, MetaDescription)
Friend Shared Function GetIndexerPropertyName(Type As Type) As String
Dim defaultAttr As Attribute = Attribute.GetCustomAttribute(Type, GetType(DefaultMemberAttribute))
Return If(TypeOf defaultAttr Is DefaultMemberAttribute, DirectCast(defaultAttr, DefaultMemberAttribute).MemberName, "")
End Function
Friend Shared Function IsCompilerGeneratedOrSystem(propOrFieldInfo As System.Reflection.MemberInfo) As Boolean
If propOrFieldInfo.Name = "Resource" Then Return True
Dim compilerGeneratedAttr As Attribute = Attribute.GetCustomAttribute(propOrFieldInfo, GetType(CompilerGeneratedAttribute))
Return TypeOf compilerGeneratedAttr Is CompilerGeneratedAttribute
End Function
Friend Shared Function GetClassDescription(type As Type) As MetaDescription
Dim result As MetaDescription = Nothing
Dim key As String = type.Assembly.GetName().Name + ":" + type.FullName
MetaDescriptor._lock.EnterUpgradeableReadLock()
If MetaDescriptor._register.ContainsKey(key) Then
result = MetaDescriptor._register(key)
MetaDescriptor._lock.ExitUpgradeableReadLock()
Else
MetaDescriptor._lock.EnterWriteLock()
MetaDescriptor._lock.ExitUpgradeableReadLock()
result = MetaDescriptor._completeMetaDescription(type)
MetaDescriptor._register.Add(key, result)
MetaDescriptor._lock.ExitWriteLock()
End If
Return result
End Function
Friend Shared Function GetColumnByCodeName(type As Type, codeColumnName As String) As Databasic.MemberInfo
Dim result As Databasic.MemberInfo = Nothing
Dim members As Dictionary(Of String, Databasic.MemberInfo)
Dim meta As MetaDescription
Dim key As String = type.Assembly.GetName().Name + ":" + type.FullName
MetaDescriptor._lock.EnterUpgradeableReadLock()
If MetaDescriptor._register.ContainsKey(key) Then
members = MetaDescriptor._register(key).ColumnsByCodeNames
If members.ContainsKey(codeColumnName) Then
result = members(codeColumnName)
End If
MetaDescriptor._lock.ExitUpgradeableReadLock()
Else
MetaDescriptor._lock.EnterWriteLock()
MetaDescriptor._lock.ExitUpgradeableReadLock()
meta = MetaDescriptor._completeMetaDescription(type)
MetaDescriptor._register.Add(key, meta)
members = meta.ColumnsByCodeNames
If members.ContainsKey(codeColumnName) Then
result = members(codeColumnName)
End If
MetaDescriptor._lock.ExitWriteLock()
End If
Return result
End Function
Friend Shared Function GetColumnsByDbNames(type As Type) As Dictionary(Of String, Databasic.MemberInfo)
Dim result As Dictionary(Of String, Databasic.MemberInfo)
Dim meta As MetaDescription
Dim key As String = type.Assembly.GetName().Name + ":" + type.FullName
MetaDescriptor._lock.EnterUpgradeableReadLock()
If MetaDescriptor._register.ContainsKey(key) Then
result = MetaDescriptor._register(key).ColumnsByDatabaseNames
MetaDescriptor._lock.ExitUpgradeableReadLock()
Else
MetaDescriptor._lock.EnterWriteLock()
MetaDescriptor._lock.ExitUpgradeableReadLock()
meta = MetaDescriptor._completeMetaDescription(type)
MetaDescriptor._register.Add(key, meta)
result = meta.ColumnsByDatabaseNames
MetaDescriptor._lock.ExitWriteLock()
End If
Return result
End Function
Friend Shared Function GetColumnsByCodeNames(type As Type) As Dictionary(Of String, Databasic.MemberInfo)
Dim result As Dictionary(Of String, Databasic.MemberInfo)
Dim meta As MetaDescription
Dim key As String = type.Assembly.GetName().Name + ":" + type.FullName
MetaDescriptor._lock.EnterUpgradeableReadLock()
If MetaDescriptor._register.ContainsKey(key) Then
result = MetaDescriptor._register(key).ColumnsByCodeNames
MetaDescriptor._lock.ExitUpgradeableReadLock()
Else
MetaDescriptor._lock.EnterWriteLock()
MetaDescriptor._lock.ExitUpgradeableReadLock()
meta = MetaDescriptor._completeMetaDescription(type)
MetaDescriptor._register.Add(key, meta)
result = meta.ColumnsByCodeNames
MetaDescriptor._lock.ExitWriteLock()
End If
Return result
End Function
Friend Shared Function GetAllKeyColumns(type As Type) As AllKeyColumns
Dim result As AllKeyColumns = New AllKeyColumns
Dim meta As MetaDescription
Dim key As String = type.Assembly.GetName().Name + ":" + type.FullName
MetaDescriptor._lock.EnterUpgradeableReadLock()
If MetaDescriptor._register.ContainsKey(key) Then
meta = MetaDescriptor._register(key)
result.PrimaryColumns = meta.PrimaryColumns
result.UniqueColumns = meta.UniqueColumns
result.AutoIncrementColumn = meta.AutoIncrementColumn
MetaDescriptor._lock.ExitUpgradeableReadLock()
Else
MetaDescriptor._lock.EnterWriteLock()
MetaDescriptor._lock.ExitUpgradeableReadLock()
meta = MetaDescriptor._completeMetaDescription(type)
MetaDescriptor._register.Add(key, meta)
result.PrimaryColumns = meta.PrimaryColumns
result.UniqueColumns = meta.UniqueColumns
result.AutoIncrementColumn = meta.AutoIncrementColumn
MetaDescriptor._lock.ExitWriteLock()
End If
Return result
End Function
Friend Shared Function GetAllKeyColumns(type As Type, keyType As KeyType) As AllKeyColumns
Dim result As AllKeyColumns = New AllKeyColumns
Dim meta As MetaDescription
Dim key As String = type.Assembly.GetName().Name + ":" + type.FullName
MetaDescriptor._lock.EnterUpgradeableReadLock()
If MetaDescriptor._register.ContainsKey(key) Then
meta = MetaDescriptor._register(key)
If keyType = KeyType.Primary Then
result.PrimaryColumns = meta.PrimaryColumns
Else
result.UniqueColumns = meta.UniqueColumns
End If
result.AutoIncrementColumn = meta.AutoIncrementColumn
MetaDescriptor._lock.ExitUpgradeableReadLock()
Else
MetaDescriptor._lock.EnterWriteLock()
MetaDescriptor._lock.ExitUpgradeableReadLock()
meta = MetaDescriptor._completeMetaDescription(type)
MetaDescriptor._register.Add(key, meta)
If keyType = KeyType.Primary Then
result.PrimaryColumns = meta.PrimaryColumns
Else
result.UniqueColumns = meta.UniqueColumns
End If
result.AutoIncrementColumn = meta.AutoIncrementColumn
MetaDescriptor._lock.ExitWriteLock()
End If
Return result
End Function
Private Shared Function _completeMetaDescription(type As Type) As MetaDescription
Dim connectionAttr As ConnectionAttribute = DirectCast(Attribute.GetCustomAttribute(type, Constants.ConnectionAttrType), ConnectionAttribute)
Dim tableAttr As TableAttribute = DirectCast(Attribute.GetCustomAttribute(type, Constants.TableAttrType), TableAttribute)
Dim result As New MetaDescription With {
.ClassType = type,
.ConnectionIndex = If(TypeOf connectionAttr Is ConnectionAttribute, connectionAttr.ConnectionIndex, Databasic.Defaults.CONNECTION_INDEX),
.Tables = If(TypeOf tableAttr Is TableAttribute, tableAttr.Tables, New String() {})
}
MetaDescriptor._completeColumnsCollections(type, result)
Return result
End Function
Private Shared Function _completeInstanceDataMembers(type As Type) As Dictionary(Of String, Databasic.MemberInfo)
Dim result As New Dictionary(Of String, Databasic.MemberInfo)
Dim indexerPropertyName As String = MetaDescriptor.GetIndexerPropertyName(type)
For Each prop As PropertyInfo In type.GetProperties(BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
If (
(Not result.ContainsKey(prop.Name)) AndAlso
prop.Name <> indexerPropertyName AndAlso
(Not MetaDescriptor.IsCompilerGeneratedOrSystem(prop))
) Then result.Add(prop.Name, New Databasic.MemberInfo With {
.MemberInfo = prop,
.MemberInfoType = MemberInfoType.Prop,
.Type = If(
Nullable.GetUnderlyingType(prop.PropertyType) <> Nothing,
Nullable.GetUnderlyingType(prop.PropertyType),
prop.PropertyType
)
})
Next
For Each field As FieldInfo In type.GetFields(BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)
If (
Not result.ContainsKey(field.Name) AndAlso
Not MetaDescriptor.IsCompilerGeneratedOrSystem(field)
) Then result.Add(field.Name, New Databasic.MemberInfo With {
.MemberInfo = field,
.MemberInfoType = MemberInfoType.Field,
.Type = If(
Nullable.GetUnderlyingType(field.FieldType) <> Nothing,
Nullable.GetUnderlyingType(field.FieldType),
field.FieldType
)
})
Next
Return result
End Function
Private Shared Sub _completeColumnsCollections(type As Type, ByRef result As MetaDescription)
Dim codeColumnName As String, dbColumnName As String,
columnAttr As ColumnAttribute,
formatAttr As FormatAttribute, formatProvider As IFormatProvider,
trimAttr As TrimAttribute, trimChars As Char(), keyName As String,
idColumnAttr As IdColumnAttribute,
autoIncrementAttr As AutoIncrementAttribute,
primaryKeyAttr As PrimaryKeyAttribute,
uniqueKeyAttr As UniqueKeyAttribute,
useEnumUnderlyingValueAttr As UseEnumUnderlyingValue,
reflMemberInfo As Reflection.MemberInfo
result.ColumnsByCodeNames = New Dictionary(Of String, Databasic.MemberInfo)
result.ColumnsByDatabaseNames = New Dictionary(Of String, Databasic.MemberInfo)
result.PrimaryColumns = New Dictionary(Of String, Dictionary(Of String, String))
result.UniqueColumns = New Dictionary(Of String, Dictionary(Of String, String))
result.AutoIncrementColumn = Nothing
Dim members As Dictionary(Of String, Databasic.MemberInfo) = MetaDescriptor._completeInstanceDataMembers(type)
For Each item In members
reflMemberInfo = item.Value.MemberInfo
columnAttr = DirectCast(Attribute.GetCustomAttribute(reflMemberInfo, Constants.ColumnAttrType), ColumnAttribute)
formatAttr = DirectCast(Attribute.GetCustomAttribute(reflMemberInfo, Constants.FormatAttrType), FormatAttribute)
trimAttr = DirectCast(Attribute.GetCustomAttribute(reflMemberInfo, Constants.TrimAttrType), TrimAttribute)
idColumnAttr = DirectCast(Attribute.GetCustomAttribute(reflMemberInfo, Constants.IdColumnAttrType), IdColumnAttribute)
autoIncrementAttr = DirectCast(Attribute.GetCustomAttribute(reflMemberInfo, Constants.AutoIncrementAttrType), AutoIncrementAttribute)
primaryKeyAttr = DirectCast(Attribute.GetCustomAttribute(reflMemberInfo, Constants.PrimaryKeyAttrType), PrimaryKeyAttribute)
uniqueKeyAttr = DirectCast(Attribute.GetCustomAttribute(reflMemberInfo, Constants.UniqueKeyAttrType), UniqueKeyAttribute)
useEnumUnderlyingValueAttr = DirectCast(Attribute.GetCustomAttribute(item.Value.Type, Constants.UseEnumUnderlyingValuesAttrType), UseEnumUnderlyingValue)
codeColumnName = reflMemberInfo.Name
dbColumnName = codeColumnName
keyName = ""
If TypeOf columnAttr Is ColumnAttribute Then
dbColumnName = If(String.IsNullOrEmpty(columnAttr.ColumnName), codeColumnName, columnAttr.ColumnName)
End If
formatProvider = If(TypeOf formatAttr Is FormatAttribute, formatAttr.FormatProvider, Nothing)
trimChars = If(TypeOf trimAttr Is TrimAttribute, trimAttr.Chars, New Char() {})
' complete code column names and db column names collections
result.ColumnsByCodeNames.Add(codeColumnName, New Databasic.MemberInfo With {
.Name = dbColumnName,
.MemberInfo = reflMemberInfo,
.Type = item.Value.Type,
.FormatProvider = formatProvider,
.TrimChars = trimChars,
.MemberInfoType = item.Value.MemberInfoType,
.Value = Nothing,
.UseEnumUnderlyingValue = TypeOf useEnumUnderlyingValueAttr Is UseEnumUnderlyingValue
})
result.ColumnsByDatabaseNames.Add(dbColumnName, New Databasic.MemberInfo With {
.Name = codeColumnName,
.MemberInfo = reflMemberInfo,
.Type = item.Value.Type,
.FormatProvider = formatProvider,
.TrimChars = trimChars,
.MemberInfoType = item.Value.MemberInfoType,
.Value = Nothing,
.UseEnumUnderlyingValue = TypeOf useEnumUnderlyingValueAttr Is UseEnumUnderlyingValue
})
' if there is any key info at class element, add it into keys info collections
If (
TypeOf primaryKeyAttr Is PrimaryKeyAttribute OrElse
TypeOf idColumnAttr Is IdColumnAttribute
) Then
If Not String.IsNullOrEmpty(primaryKeyAttr.KeyName) Then keyName = primaryKeyAttr.KeyName
If result.PrimaryColumns.ContainsKey(keyName) Then
result.PrimaryColumns(keyName).Add(codeColumnName, dbColumnName)
Else
result.PrimaryColumns.Add(keyName, New Dictionary(Of String, String)() From {
{codeColumnName, dbColumnName}
})
End If
End If
If TypeOf uniqueKeyAttr Is UniqueKeyAttribute Then
If Not String.IsNullOrEmpty(uniqueKeyAttr.KeyName) Then keyName = uniqueKeyAttr.KeyName
If result.UniqueColumns.ContainsKey(keyName) Then
result.UniqueColumns(keyName).Add(codeColumnName, dbColumnName)
Else
result.UniqueColumns.Add(keyName, New Dictionary(Of String, String)() From {
{codeColumnName, dbColumnName}
})
End If
End If
If (
TypeOf autoIncrementAttr Is AutoIncrementAttribute OrElse
TypeOf idColumnAttr Is IdColumnAttribute
) Then
If Not result.AutoIncrementColumn.HasValue Then
result.AutoIncrementColumn = New AutoIncrementColumn With {
.CodeColumnName = codeColumnName,
.DatabaseColumnName = dbColumnName
}
Else
Throw New Exception(String.Format(
"Class '{0}' has defined multiple autoincrement members. " +
"Plase define only one field or property with attribute '{1}'.",
type.FullName, "AutoIncrement"
))
End If
End If
Next
End Sub
End Class