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