1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Linq ;
4
+ using System . Reflection ;
5
+
6
+ namespace MongoDB . Bson . Serialization ;
7
+
8
+ internal class BsonClassMapDomain : IBsonClassMapDomain
9
+ {
10
+ // private fields
11
+ private readonly Dictionary < Type , BsonClassMap > _classMaps = new ( ) ;
12
+
13
+ /// <summary>
14
+ /// Gets all registered class maps.
15
+ /// </summary>
16
+ /// <returns>All registered class maps.</returns>
17
+ public IEnumerable < BsonClassMap > GetRegisteredClassMaps ( )
18
+ {
19
+ BsonSerializer . ConfigLock . EnterReadLock ( ) ; //TODO It would make sense to look at this after the PR by Robert is merged
20
+ try
21
+ {
22
+ return _classMaps . Values . ToList ( ) ; // return a copy for thread safety
23
+ }
24
+ finally
25
+ {
26
+ BsonSerializer . ConfigLock . ExitReadLock ( ) ;
27
+ }
28
+ }
29
+
30
+ /// <summary>
31
+ /// Checks whether a class map is registered for a type.
32
+ /// </summary>
33
+ /// <param name="type">The type to check.</param>
34
+ /// <returns>True if there is a class map registered for the type.</returns>
35
+ public bool IsClassMapRegistered ( Type type )
36
+ {
37
+ if ( type == null )
38
+ {
39
+ throw new ArgumentNullException ( "type" ) ;
40
+ }
41
+
42
+ BsonSerializer . ConfigLock . EnterReadLock ( ) ;
43
+ try
44
+ {
45
+ return _classMaps . ContainsKey ( type ) ;
46
+ }
47
+ finally
48
+ {
49
+ BsonSerializer . ConfigLock . ExitReadLock ( ) ;
50
+ }
51
+ }
52
+
53
+ /// <summary>
54
+ /// Looks up a class map (will AutoMap the class if no class map is registered).
55
+ /// </summary>
56
+ /// <param name="classType">The class type.</param>
57
+ /// <returns>The class map.</returns>
58
+ public BsonClassMap LookupClassMap ( Type classType )
59
+ {
60
+ if ( classType == null )
61
+ {
62
+ throw new ArgumentNullException ( "classType" ) ;
63
+ }
64
+
65
+ BsonSerializer . ConfigLock . EnterReadLock ( ) ;
66
+ try
67
+ {
68
+ if ( _classMaps . TryGetValue ( classType , out var classMap ) )
69
+ {
70
+ if ( classMap . IsFrozen )
71
+ {
72
+ return classMap ;
73
+ }
74
+ }
75
+ }
76
+ finally
77
+ {
78
+ BsonSerializer . ConfigLock . ExitReadLock ( ) ;
79
+ }
80
+
81
+ // automatically create a new classMap for classType and register it (unless another thread does first)
82
+ // do the work of speculatively creating a new class map outside of holding any lock
83
+ var classMapDefinition = typeof ( BsonClassMap < > ) ;
84
+ var classMapType = classMapDefinition . MakeGenericType ( classType ) ;
85
+ var newClassMap = ( BsonClassMap ) Activator . CreateInstance ( classMapType ) ;
86
+ newClassMap . AutoMap ( ) ;
87
+
88
+ BsonSerializer . ConfigLock . EnterWriteLock ( ) ;
89
+ try
90
+ {
91
+ if ( ! _classMaps . TryGetValue ( classType , out var classMap ) )
92
+ {
93
+ RegisterClassMap ( newClassMap ) ;
94
+ classMap = newClassMap ;
95
+ }
96
+
97
+ return classMap . Freeze ( ) ;
98
+ }
99
+ finally
100
+ {
101
+ BsonSerializer . ConfigLock . ExitWriteLock ( ) ;
102
+ }
103
+ }
104
+
105
+ /// <summary>
106
+ /// Creates and registers a class map.
107
+ /// </summary>
108
+ /// <typeparam name="TClass">The class.</typeparam>
109
+ /// <returns>The class map.</returns>
110
+ public BsonClassMap < TClass > RegisterClassMap < TClass > ( )
111
+ {
112
+ return RegisterClassMap < TClass > ( cm => { cm . AutoMap ( ) ; } ) ;
113
+ }
114
+
115
+ /// <summary>
116
+ /// Creates and registers a class map.
117
+ /// </summary>
118
+ /// <typeparam name="TClass">The class.</typeparam>
119
+ /// <param name="classMapInitializer">The class map initializer.</param>
120
+ /// <returns>The class map.</returns>
121
+ public BsonClassMap < TClass > RegisterClassMap < TClass > ( Action < BsonClassMap < TClass > > classMapInitializer )
122
+ {
123
+ var classMap = new BsonClassMap < TClass > ( classMapInitializer ) ;
124
+ RegisterClassMap ( classMap ) ;
125
+ return classMap ;
126
+ }
127
+
128
+ /// <summary>
129
+ /// Registers a class map.
130
+ /// </summary>
131
+ /// <param name="classMap">The class map.</param>
132
+ public void RegisterClassMap ( BsonClassMap classMap )
133
+ {
134
+ if ( classMap == null )
135
+ {
136
+ throw new ArgumentNullException ( "classMap" ) ;
137
+ }
138
+
139
+ BsonSerializer . ConfigLock . EnterWriteLock ( ) ;
140
+ try
141
+ {
142
+ // note: class maps can NOT be replaced (because derived classes refer to existing instance)
143
+ _classMaps . Add ( classMap . ClassType , classMap ) ;
144
+ BsonSerializer . RegisterDiscriminator ( classMap . ClassType , classMap . Discriminator ) ;
145
+ }
146
+ finally
147
+ {
148
+ BsonSerializer . ConfigLock . ExitWriteLock ( ) ;
149
+ }
150
+ }
151
+
152
+ /// <summary>
153
+ /// Registers a class map if it is not already registered.
154
+ /// </summary>
155
+ /// <typeparam name="TClass">The class.</typeparam>
156
+ /// <returns>True if this call registered the class map, false if the class map was already registered.</returns>
157
+ public bool TryRegisterClassMap < TClass > ( )
158
+ {
159
+ return TryRegisterClassMap ( ClassMapFactory ) ;
160
+
161
+ static BsonClassMap < TClass > ClassMapFactory ( )
162
+ {
163
+ var classMap = new BsonClassMap < TClass > ( ) ;
164
+ classMap . AutoMap ( ) ;
165
+ return classMap ;
166
+ }
167
+ }
168
+
169
+ /// <summary>
170
+ /// Registers a class map if it is not already registered.
171
+ /// </summary>
172
+ /// <typeparam name="TClass">The class.</typeparam>
173
+ /// <param name="classMap">The class map.</param>
174
+ /// <returns>True if this call registered the class map, false if the class map was already registered.</returns>
175
+ public bool TryRegisterClassMap < TClass > ( BsonClassMap < TClass > classMap )
176
+ {
177
+ if ( classMap == null )
178
+ {
179
+ throw new ArgumentNullException ( nameof ( classMap ) ) ;
180
+ }
181
+
182
+ return TryRegisterClassMap ( ClassMapFactory ) ;
183
+
184
+ BsonClassMap < TClass > ClassMapFactory ( )
185
+ {
186
+ return classMap ;
187
+ }
188
+ }
189
+
190
+ /// <summary>
191
+ /// Registers a class map if it is not already registered.
192
+ /// </summary>
193
+ /// <typeparam name="TClass">The class.</typeparam>
194
+ /// <param name="classMapInitializer">The class map initializer (only called if the class map is not already registered).</param>
195
+ /// <returns>True if this call registered the class map, false if the class map was already registered.</returns>
196
+ public bool TryRegisterClassMap < TClass > ( Action < BsonClassMap < TClass > > classMapInitializer )
197
+ {
198
+ if ( classMapInitializer == null )
199
+ {
200
+ throw new ArgumentNullException ( nameof ( classMapInitializer ) ) ;
201
+ }
202
+
203
+ return TryRegisterClassMap ( ClassMapFactory ) ;
204
+
205
+ BsonClassMap < TClass > ClassMapFactory ( )
206
+ {
207
+ return new BsonClassMap < TClass > ( classMapInitializer ) ;
208
+ }
209
+ }
210
+
211
+ /// <summary>
212
+ /// Registers a class map if it is not already registered.
213
+ /// </summary>
214
+ /// <typeparam name="TClass">The class.</typeparam>
215
+ /// <param name="classMapFactory">The class map factory (only called if the class map is not already registered).</param>
216
+ /// <returns>True if this call registered the class map, false if the class map was already registered.</returns>
217
+ public bool TryRegisterClassMap < TClass > ( Func < BsonClassMap < TClass > > classMapFactory )
218
+ {
219
+ if ( classMapFactory == null )
220
+ {
221
+ throw new ArgumentNullException ( nameof ( classMapFactory ) ) ;
222
+ }
223
+
224
+ BsonSerializer . ConfigLock . EnterReadLock ( ) ;
225
+ try
226
+ {
227
+ if ( _classMaps . ContainsKey ( typeof ( TClass ) ) )
228
+ {
229
+ return false ;
230
+ }
231
+ }
232
+ finally
233
+ {
234
+ BsonSerializer . ConfigLock . ExitReadLock ( ) ;
235
+ }
236
+
237
+ BsonSerializer . ConfigLock . EnterWriteLock ( ) ;
238
+ try
239
+ {
240
+ if ( _classMaps . ContainsKey ( typeof ( TClass ) ) )
241
+ {
242
+ return false ;
243
+ }
244
+ else
245
+ {
246
+ // create a classMap for TClass and register it
247
+ var classMap = classMapFactory ( ) ;
248
+ RegisterClassMap ( classMap ) ;
249
+ return true ;
250
+ }
251
+ }
252
+ finally
253
+ {
254
+ BsonSerializer . ConfigLock . ExitWriteLock ( ) ;
255
+ }
256
+ }
257
+ }
0 commit comments