BsonClassMap.cs 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669
  1. /* Copyright 2010-2016 MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Collections.ObjectModel;
  18. using System.Linq;
  19. using System.Linq.Expressions;
  20. using System.Reflection;
  21. using System.Reflection.Emit;
  22. using System.Runtime.CompilerServices;
  23. #if NET45
  24. using System.Runtime.Serialization;
  25. #endif
  26. using MongoDB.Bson.IO;
  27. using MongoDB.Bson.Serialization.Conventions;
  28. namespace MongoDB.Bson.Serialization
  29. {
  30. /// <summary>
  31. /// Represents a mapping between a class and a BSON document.
  32. /// </summary>
  33. public class BsonClassMap
  34. {
  35. // private static fields
  36. private readonly static Dictionary<Type, BsonClassMap> __classMaps = new Dictionary<Type, BsonClassMap>();
  37. private readonly static Queue<Type> __knownTypesQueue = new Queue<Type>();
  38. private static readonly MethodInfo __getUninitializedObjectMethodInfo =
  39. typeof(string)
  40. .GetTypeInfo()
  41. .Assembly
  42. .GetType("System.Runtime.Serialization.FormatterServices")
  43. .GetTypeInfo()
  44. ?.GetMethod("GetUninitializedObject", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
  45. private static int __freezeNestingLevel = 0;
  46. // private fields
  47. private readonly Type _classType;
  48. private readonly List<BsonCreatorMap> _creatorMaps;
  49. private readonly IConventionPack _conventionPack;
  50. private readonly bool _isAnonymous;
  51. private readonly List<BsonMemberMap> _allMemberMaps; // includes inherited member maps
  52. private readonly ReadOnlyCollection<BsonMemberMap> _allMemberMapsReadonly;
  53. private readonly List<BsonMemberMap> _declaredMemberMaps; // only the members declared in this class
  54. private readonly BsonTrie<int> _elementTrie;
  55. private bool _frozen; // once a class map has been frozen no further changes are allowed
  56. private BsonClassMap _baseClassMap; // null for class object and interfaces
  57. private volatile IDiscriminatorConvention _discriminatorConvention;
  58. private Func<object> _creator;
  59. private string _discriminator;
  60. private bool _discriminatorIsRequired;
  61. private bool _hasRootClass;
  62. private bool _isRootClass;
  63. private BsonMemberMap _idMemberMap;
  64. private bool _ignoreExtraElements;
  65. private bool _ignoreExtraElementsIsInherited;
  66. private BsonMemberMap _extraElementsMemberMap;
  67. private int _extraElementsMemberIndex = -1;
  68. private List<Type> _knownTypes = new List<Type>();
  69. // constructors
  70. /// <summary>
  71. /// Initializes a new instance of the BsonClassMap class.
  72. /// </summary>
  73. /// <param name="classType">The class type.</param>
  74. public BsonClassMap(Type classType)
  75. {
  76. _classType = classType;
  77. _creatorMaps = new List<BsonCreatorMap>();
  78. _conventionPack = ConventionRegistry.Lookup(classType);
  79. _isAnonymous = IsAnonymousType(classType);
  80. _allMemberMaps = new List<BsonMemberMap>();
  81. _allMemberMapsReadonly = _allMemberMaps.AsReadOnly();
  82. _declaredMemberMaps = new List<BsonMemberMap>();
  83. _elementTrie = new BsonTrie<int>();
  84. Reset();
  85. }
  86. /// <summary>
  87. /// Initializes a new instance of the <see cref="BsonClassMap"/> class.
  88. /// </summary>
  89. /// <param name="classType">Type of the class.</param>
  90. /// <param name="baseClassMap">The base class map.</param>
  91. public BsonClassMap(Type classType, BsonClassMap baseClassMap)
  92. : this(classType)
  93. {
  94. _baseClassMap = baseClassMap;
  95. }
  96. // public properties
  97. /// <summary>
  98. /// Gets all the member maps (including maps for inherited members).
  99. /// </summary>
  100. public ReadOnlyCollection<BsonMemberMap> AllMemberMaps
  101. {
  102. get { return _allMemberMapsReadonly; }
  103. }
  104. /// <summary>
  105. /// Gets the base class map.
  106. /// </summary>
  107. public BsonClassMap BaseClassMap
  108. {
  109. get { return _baseClassMap; }
  110. }
  111. /// <summary>
  112. /// Gets the class type.
  113. /// </summary>
  114. public Type ClassType
  115. {
  116. get { return _classType; }
  117. }
  118. /// <summary>
  119. /// Gets the constructor maps.
  120. /// </summary>
  121. public IEnumerable<BsonCreatorMap> CreatorMaps
  122. {
  123. get { return _creatorMaps; }
  124. }
  125. /// <summary>
  126. /// Gets the conventions used for auto mapping.
  127. /// </summary>
  128. public IConventionPack ConventionPack
  129. {
  130. get { return _conventionPack; }
  131. }
  132. /// <summary>
  133. /// Gets the declared member maps (only for members declared in this class).
  134. /// </summary>
  135. public IEnumerable<BsonMemberMap> DeclaredMemberMaps
  136. {
  137. get { return _declaredMemberMaps; }
  138. }
  139. /// <summary>
  140. /// Gets the discriminator.
  141. /// </summary>
  142. public string Discriminator
  143. {
  144. get { return _discriminator; }
  145. }
  146. /// <summary>
  147. /// Gets whether a discriminator is required when serializing this class.
  148. /// </summary>
  149. public bool DiscriminatorIsRequired
  150. {
  151. get { return _discriminatorIsRequired; }
  152. }
  153. /// <summary>
  154. /// Gets the member map of the member used to hold extra elements.
  155. /// </summary>
  156. public BsonMemberMap ExtraElementsMemberMap
  157. {
  158. get { return _extraElementsMemberMap; }
  159. }
  160. /// <summary>
  161. /// Gets whether this class map has any creator maps.
  162. /// </summary>
  163. public bool HasCreatorMaps
  164. {
  165. get { return _creatorMaps.Count > 0; }
  166. }
  167. /// <summary>
  168. /// Gets whether this class has a root class ancestor.
  169. /// </summary>
  170. public bool HasRootClass
  171. {
  172. get { return _hasRootClass; }
  173. }
  174. /// <summary>
  175. /// Gets the Id member map (null if none).
  176. /// </summary>
  177. public BsonMemberMap IdMemberMap
  178. {
  179. get { return _idMemberMap; }
  180. }
  181. /// <summary>
  182. /// Gets whether extra elements should be ignored when deserializing.
  183. /// </summary>
  184. public bool IgnoreExtraElements
  185. {
  186. get { return _ignoreExtraElements; }
  187. }
  188. /// <summary>
  189. /// Gets whether the IgnoreExtraElements value should be inherited by derived classes.
  190. /// </summary>
  191. public bool IgnoreExtraElementsIsInherited
  192. {
  193. get { return _ignoreExtraElementsIsInherited; }
  194. }
  195. /// <summary>
  196. /// Gets whether this class is anonymous.
  197. /// </summary>
  198. public bool IsAnonymous
  199. {
  200. get { return _isAnonymous; }
  201. }
  202. /// <summary>
  203. /// Gets whether the class map is frozen.
  204. /// </summary>
  205. public bool IsFrozen
  206. {
  207. get { return _frozen; }
  208. }
  209. /// <summary>
  210. /// Gets whether this class is a root class.
  211. /// </summary>
  212. public bool IsRootClass
  213. {
  214. get { return _isRootClass; }
  215. }
  216. /// <summary>
  217. /// Gets the known types of this class.
  218. /// </summary>
  219. public IEnumerable<Type> KnownTypes
  220. {
  221. get { return _knownTypes; }
  222. }
  223. // internal properties
  224. /// <summary>
  225. /// Gets the element name to member index trie.
  226. /// </summary>
  227. internal BsonTrie<int> ElementTrie
  228. {
  229. get { return _elementTrie; }
  230. }
  231. /// <summary>
  232. /// Gets the member index of the member used to hold extra elements.
  233. /// </summary>
  234. internal int ExtraElementsMemberMapIndex
  235. {
  236. get { return _extraElementsMemberIndex; }
  237. }
  238. // public static methods
  239. /// <summary>
  240. /// Gets the type of a member.
  241. /// </summary>
  242. /// <param name="memberInfo">The member info.</param>
  243. /// <returns>The type of the member.</returns>
  244. public static Type GetMemberInfoType(MemberInfo memberInfo)
  245. {
  246. if (memberInfo == null)
  247. {
  248. throw new ArgumentNullException("memberInfo");
  249. }
  250. if (memberInfo is FieldInfo)
  251. {
  252. return ((FieldInfo)memberInfo).FieldType;
  253. }
  254. else if (memberInfo is PropertyInfo)
  255. {
  256. return ((PropertyInfo)memberInfo).PropertyType;
  257. }
  258. throw new NotSupportedException("Only field and properties are supported at this time.");
  259. }
  260. /// <summary>
  261. /// Gets all registered class maps.
  262. /// </summary>
  263. /// <returns>All registered class maps.</returns>
  264. public static IEnumerable<BsonClassMap> GetRegisteredClassMaps()
  265. {
  266. BsonSerializer.ConfigLock.EnterReadLock();
  267. try
  268. {
  269. return __classMaps.Values.ToList(); // return a copy for thread safety
  270. }
  271. finally
  272. {
  273. BsonSerializer.ConfigLock.ExitReadLock();
  274. }
  275. }
  276. /// <summary>
  277. /// Checks whether a class map is registered for a type.
  278. /// </summary>
  279. /// <param name="type">The type to check.</param>
  280. /// <returns>True if there is a class map registered for the type.</returns>
  281. public static bool IsClassMapRegistered(Type type)
  282. {
  283. if (type == null)
  284. {
  285. throw new ArgumentNullException("type");
  286. }
  287. BsonSerializer.ConfigLock.EnterReadLock();
  288. try
  289. {
  290. return __classMaps.ContainsKey(type);
  291. }
  292. finally
  293. {
  294. BsonSerializer.ConfigLock.ExitReadLock();
  295. }
  296. }
  297. /// <summary>
  298. /// Looks up a class map (will AutoMap the class if no class map is registered).
  299. /// </summary>
  300. /// <param name="classType">The class type.</param>
  301. /// <returns>The class map.</returns>
  302. public static BsonClassMap LookupClassMap(Type classType)
  303. {
  304. if (classType == null)
  305. {
  306. throw new ArgumentNullException("classType");
  307. }
  308. BsonSerializer.ConfigLock.EnterReadLock();
  309. try
  310. {
  311. BsonClassMap classMap;
  312. if (__classMaps.TryGetValue(classType, out classMap))
  313. {
  314. if (classMap.IsFrozen)
  315. {
  316. return classMap;
  317. }
  318. }
  319. }
  320. finally
  321. {
  322. BsonSerializer.ConfigLock.ExitReadLock();
  323. }
  324. BsonSerializer.ConfigLock.EnterWriteLock();
  325. try
  326. {
  327. BsonClassMap classMap;
  328. if (!__classMaps.TryGetValue(classType, out classMap))
  329. {
  330. // automatically create a classMap for classType and register it
  331. var classMapDefinition = typeof(BsonClassMap<>);
  332. var classMapType = classMapDefinition.MakeGenericType(classType);
  333. classMap = (BsonClassMap)Activator.CreateInstance(classMapType);
  334. classMap.AutoMap();
  335. RegisterClassMap(classMap);
  336. }
  337. return classMap.Freeze();
  338. }
  339. finally
  340. {
  341. BsonSerializer.ConfigLock.ExitWriteLock();
  342. }
  343. }
  344. /// <summary>
  345. /// Creates and registers a class map.
  346. /// </summary>
  347. /// <typeparam name="TClass">The class.</typeparam>
  348. /// <returns>The class map.</returns>
  349. public static BsonClassMap<TClass> RegisterClassMap<TClass>()
  350. {
  351. return RegisterClassMap<TClass>(cm => { cm.AutoMap(); });
  352. }
  353. /// <summary>
  354. /// Creates and registers a class map.
  355. /// </summary>
  356. /// <typeparam name="TClass">The class.</typeparam>
  357. /// <param name="classMapInitializer">The class map initializer.</param>
  358. /// <returns>The class map.</returns>
  359. public static BsonClassMap<TClass> RegisterClassMap<TClass>(Action<BsonClassMap<TClass>> classMapInitializer)
  360. {
  361. var classMap = new BsonClassMap<TClass>(classMapInitializer);
  362. RegisterClassMap(classMap);
  363. return classMap;
  364. }
  365. /// <summary>
  366. /// Registers a class map.
  367. /// </summary>
  368. /// <param name="classMap">The class map.</param>
  369. public static void RegisterClassMap(BsonClassMap classMap)
  370. {
  371. if (classMap == null)
  372. {
  373. throw new ArgumentNullException("classMap");
  374. }
  375. BsonSerializer.ConfigLock.EnterWriteLock();
  376. try
  377. {
  378. // note: class maps can NOT be replaced (because derived classes refer to existing instance)
  379. __classMaps.Add(classMap.ClassType, classMap);
  380. BsonSerializer.RegisterDiscriminator(classMap.ClassType, classMap.Discriminator);
  381. }
  382. finally
  383. {
  384. BsonSerializer.ConfigLock.ExitWriteLock();
  385. }
  386. }
  387. // public methods
  388. /// <summary>
  389. /// Automaps the class.
  390. /// </summary>
  391. public void AutoMap()
  392. {
  393. if (_frozen) { ThrowFrozenException(); }
  394. AutoMapClass();
  395. }
  396. /// <summary>
  397. /// Creates an instance of the class.
  398. /// </summary>
  399. /// <returns>An object.</returns>
  400. public object CreateInstance()
  401. {
  402. if (!_frozen) { ThrowNotFrozenException(); }
  403. var creator = GetCreator();
  404. return creator.Invoke();
  405. }
  406. /// <summary>
  407. /// Freezes the class map.
  408. /// </summary>
  409. /// <returns>The frozen class map.</returns>
  410. public BsonClassMap Freeze()
  411. {
  412. BsonSerializer.ConfigLock.EnterReadLock();
  413. try
  414. {
  415. if (_frozen)
  416. {
  417. return this;
  418. }
  419. }
  420. finally
  421. {
  422. BsonSerializer.ConfigLock.ExitReadLock();
  423. }
  424. BsonSerializer.ConfigLock.EnterWriteLock();
  425. try
  426. {
  427. if (!_frozen)
  428. {
  429. __freezeNestingLevel++;
  430. try
  431. {
  432. var baseType = _classType.GetTypeInfo().BaseType;
  433. if (baseType != null)
  434. {
  435. if (_baseClassMap == null)
  436. {
  437. _baseClassMap = LookupClassMap(baseType);
  438. }
  439. _discriminatorIsRequired |= _baseClassMap._discriminatorIsRequired;
  440. _hasRootClass |= (_isRootClass || _baseClassMap.HasRootClass);
  441. _allMemberMaps.AddRange(_baseClassMap.AllMemberMaps);
  442. if (_baseClassMap.IgnoreExtraElements && _baseClassMap.IgnoreExtraElementsIsInherited)
  443. {
  444. _ignoreExtraElements = true;
  445. _ignoreExtraElementsIsInherited = true;
  446. }
  447. }
  448. _allMemberMaps.AddRange(_declaredMemberMaps);
  449. if (_idMemberMap == null)
  450. {
  451. // see if we can inherit the idMemberMap from our base class
  452. if (_baseClassMap != null)
  453. {
  454. _idMemberMap = _baseClassMap.IdMemberMap;
  455. }
  456. }
  457. else
  458. {
  459. if (_idMemberMap.ClassMap == this)
  460. {
  461. // conventions could have set this to an improper value
  462. _idMemberMap.SetElementName("_id");
  463. }
  464. }
  465. if (_extraElementsMemberMap == null)
  466. {
  467. // see if we can inherit the extraElementsMemberMap from our base class
  468. if (_baseClassMap != null)
  469. {
  470. _extraElementsMemberMap = _baseClassMap.ExtraElementsMemberMap;
  471. }
  472. }
  473. _extraElementsMemberIndex = -1;
  474. for (int memberIndex = 0; memberIndex < _allMemberMaps.Count; ++memberIndex)
  475. {
  476. var memberMap = _allMemberMaps[memberIndex];
  477. int conflictingMemberIndex;
  478. if (!_elementTrie.TryGetValue(memberMap.ElementName, out conflictingMemberIndex))
  479. {
  480. _elementTrie.Add(memberMap.ElementName, memberIndex);
  481. }
  482. else
  483. {
  484. var conflictingMemberMap = _allMemberMaps[conflictingMemberIndex];
  485. var fieldOrProperty = (memberMap.MemberInfo is FieldInfo) ? "field" : "property";
  486. var conflictingFieldOrProperty = (conflictingMemberMap.MemberInfo is FieldInfo) ? "field" : "property";
  487. var conflictingType = conflictingMemberMap.MemberInfo.DeclaringType;
  488. string message;
  489. if (conflictingType == _classType)
  490. {
  491. message = string.Format(
  492. "The {0} '{1}' of type '{2}' cannot use element name '{3}' because it is already being used by {4} '{5}'.",
  493. fieldOrProperty, memberMap.MemberName, _classType.FullName, memberMap.ElementName, conflictingFieldOrProperty, conflictingMemberMap.MemberName);
  494. }
  495. else
  496. {
  497. message = string.Format(
  498. "The {0} '{1}' of type '{2}' cannot use element name '{3}' because it is already being used by {4} '{5}' of type '{6}'.",
  499. fieldOrProperty, memberMap.MemberName, _classType.FullName, memberMap.ElementName, conflictingFieldOrProperty, conflictingMemberMap.MemberName, conflictingType.FullName);
  500. }
  501. throw new BsonSerializationException(message);
  502. }
  503. if (memberMap == _extraElementsMemberMap)
  504. {
  505. _extraElementsMemberIndex = memberIndex;
  506. }
  507. }
  508. // mark this classMap frozen before we start working on knownTypes
  509. // because we might get back to this same classMap while processing knownTypes
  510. foreach (var creatorMap in _creatorMaps)
  511. {
  512. creatorMap.Freeze();
  513. }
  514. foreach (var memberMap in _declaredMemberMaps)
  515. {
  516. memberMap.Freeze();
  517. }
  518. _frozen = true;
  519. // use a queue to postpone processing of known types until we get back to the first level call to Freeze
  520. // this avoids infinite recursion when going back down the inheritance tree while processing known types
  521. foreach (var knownType in _knownTypes)
  522. {
  523. __knownTypesQueue.Enqueue(knownType);
  524. }
  525. // if we are back to the first level go ahead and process any queued known types
  526. if (__freezeNestingLevel == 1)
  527. {
  528. while (__knownTypesQueue.Count != 0)
  529. {
  530. var knownType = __knownTypesQueue.Dequeue();
  531. LookupClassMap(knownType); // will AutoMap and/or Freeze knownType if necessary
  532. }
  533. }
  534. }
  535. finally
  536. {
  537. __freezeNestingLevel--;
  538. }
  539. }
  540. }
  541. finally
  542. {
  543. BsonSerializer.ConfigLock.ExitWriteLock();
  544. }
  545. return this;
  546. }
  547. /// <summary>
  548. /// Gets a member map (only considers members declared in this class).
  549. /// </summary>
  550. /// <param name="memberName">The member name.</param>
  551. /// <returns>The member map (or null if the member was not found).</returns>
  552. public BsonMemberMap GetMemberMap(string memberName)
  553. {
  554. if (memberName == null)
  555. {
  556. throw new ArgumentNullException("memberName");
  557. }
  558. // can be called whether frozen or not
  559. return _declaredMemberMaps.Find(m => m.MemberName == memberName);
  560. }
  561. /// <summary>
  562. /// Gets the member map for a BSON element.
  563. /// </summary>
  564. /// <param name="elementName">The name of the element.</param>
  565. /// <returns>The member map.</returns>
  566. public BsonMemberMap GetMemberMapForElement(string elementName)
  567. {
  568. if (elementName == null)
  569. {
  570. throw new ArgumentNullException("elementName");
  571. }
  572. if (!_frozen) { ThrowNotFrozenException(); }
  573. int memberIndex;
  574. if (!_elementTrie.TryGetValue(elementName, out memberIndex))
  575. {
  576. return null;
  577. }
  578. var memberMap = _allMemberMaps[memberIndex];
  579. return memberMap;
  580. }
  581. /// <summary>
  582. /// Creates a creator map for a constructor and adds it to the class map.
  583. /// </summary>
  584. /// <param name="constructorInfo">The constructor info.</param>
  585. /// <returns>The creator map (so method calls can be chained).</returns>
  586. public BsonCreatorMap MapConstructor(ConstructorInfo constructorInfo)
  587. {
  588. if (constructorInfo == null)
  589. {
  590. throw new ArgumentNullException("constructorInfo");
  591. }
  592. EnsureMemberInfoIsForThisClass(constructorInfo);
  593. if (_frozen) { ThrowFrozenException(); }
  594. var creatorMap = _creatorMaps.FirstOrDefault(m => m.MemberInfo == constructorInfo);
  595. if (creatorMap == null)
  596. {
  597. var @delegate = new CreatorMapDelegateCompiler().CompileConstructorDelegate(constructorInfo);
  598. creatorMap = new BsonCreatorMap(this, constructorInfo, @delegate);
  599. _creatorMaps.Add(creatorMap);
  600. }
  601. return creatorMap;
  602. }
  603. /// <summary>
  604. /// Creates a creator map for a constructor and adds it to the class map.
  605. /// </summary>
  606. /// <param name="constructorInfo">The constructor info.</param>
  607. /// <param name="argumentNames">The argument names.</param>
  608. /// <returns>The creator map (so method calls can be chained).</returns>
  609. public BsonCreatorMap MapConstructor(ConstructorInfo constructorInfo, params string[] argumentNames)
  610. {
  611. var creatorMap = MapConstructor(constructorInfo);
  612. creatorMap.SetArguments(argumentNames);
  613. return creatorMap;
  614. }
  615. /// <summary>
  616. /// Creates a creator map and adds it to the class.
  617. /// </summary>
  618. /// <param name="delegate">The delegate.</param>
  619. /// <returns>The factory method map (so method calls can be chained).</returns>
  620. public BsonCreatorMap MapCreator(Delegate @delegate)
  621. {
  622. if (@delegate == null)
  623. {
  624. throw new ArgumentNullException("delegate");
  625. }
  626. if (_frozen) { ThrowFrozenException(); }
  627. var creatorMap = new BsonCreatorMap(this, null, @delegate);
  628. _creatorMaps.Add(creatorMap);
  629. return creatorMap;
  630. }
  631. /// <summary>
  632. /// Creates a creator map and adds it to the class.
  633. /// </summary>
  634. /// <param name="delegate">The delegate.</param>
  635. /// <param name="argumentNames">The argument names.</param>
  636. /// <returns>The factory method map (so method calls can be chained).</returns>
  637. public BsonCreatorMap MapCreator(Delegate @delegate, params string[] argumentNames)
  638. {
  639. var creatorMap = MapCreator(@delegate);
  640. creatorMap.SetArguments(argumentNames);
  641. return creatorMap;
  642. }
  643. /// <summary>
  644. /// Creates a member map for the extra elements field and adds it to the class map.
  645. /// </summary>
  646. /// <param name="fieldName">The name of the extra elements field.</param>
  647. /// <returns>The member map (so method calls can be chained).</returns>
  648. public BsonMemberMap MapExtraElementsField(string fieldName)
  649. {
  650. if (fieldName == null)
  651. {
  652. throw new ArgumentNullException("fieldName");
  653. }
  654. if (_frozen) { ThrowFrozenException(); }
  655. var fieldMap = MapField(fieldName);
  656. SetExtraElementsMember(fieldMap);
  657. return fieldMap;
  658. }
  659. /// <summary>
  660. /// Creates a member map for the extra elements member and adds it to the class map.
  661. /// </summary>
  662. /// <param name="memberInfo">The member info for the extra elements member.</param>
  663. /// <returns>The member map (so method calls can be chained).</returns>
  664. public BsonMemberMap MapExtraElementsMember(MemberInfo memberInfo)
  665. {
  666. if (memberInfo == null)
  667. {
  668. throw new ArgumentNullException("memberInfo");
  669. }
  670. if (_frozen) { ThrowFrozenException(); }
  671. var memberMap = MapMember(memberInfo);
  672. SetExtraElementsMember(memberMap);
  673. return memberMap;
  674. }
  675. /// <summary>
  676. /// Creates a member map for the extra elements property and adds it to the class map.
  677. /// </summary>
  678. /// <param name="propertyName">The name of the property.</param>
  679. /// <returns>The member map (so method calls can be chained).</returns>
  680. public BsonMemberMap MapExtraElementsProperty(string propertyName)
  681. {
  682. if (propertyName == null)
  683. {
  684. throw new ArgumentNullException("propertyName");
  685. }
  686. if (_frozen) { ThrowFrozenException(); }
  687. var propertyMap = MapProperty(propertyName);
  688. SetExtraElementsMember(propertyMap);
  689. return propertyMap;
  690. }
  691. /// <summary>
  692. /// Creates a creator map for a factory method and adds it to the class.
  693. /// </summary>
  694. /// <param name="methodInfo">The method info.</param>
  695. /// <returns>The creator map (so method calls can be chained).</returns>
  696. public BsonCreatorMap MapFactoryMethod(MethodInfo methodInfo)
  697. {
  698. if (methodInfo == null)
  699. {
  700. throw new ArgumentNullException("methodInfo");
  701. }
  702. EnsureMemberInfoIsForThisClass(methodInfo);
  703. if (_frozen) { ThrowFrozenException(); }
  704. var creatorMap = _creatorMaps.FirstOrDefault(m => m.MemberInfo == methodInfo);
  705. if (creatorMap == null)
  706. {
  707. var @delegate = new CreatorMapDelegateCompiler().CompileFactoryMethodDelegate(methodInfo);
  708. creatorMap = new BsonCreatorMap(this, methodInfo, @delegate);
  709. _creatorMaps.Add(creatorMap);
  710. }
  711. return creatorMap;
  712. }
  713. /// <summary>
  714. /// Creates a creator map for a factory method and adds it to the class.
  715. /// </summary>
  716. /// <param name="methodInfo">The method info.</param>
  717. /// <param name="argumentNames">The argument names.</param>
  718. /// <returns>The creator map (so method calls can be chained).</returns>
  719. public BsonCreatorMap MapFactoryMethod(MethodInfo methodInfo, params string[] argumentNames)
  720. {
  721. var creatorMap = MapFactoryMethod(methodInfo);
  722. creatorMap.SetArguments(argumentNames);
  723. return creatorMap;
  724. }
  725. /// <summary>
  726. /// Creates a member map for a field and adds it to the class map.
  727. /// </summary>
  728. /// <param name="fieldName">The name of the field.</param>
  729. /// <returns>The member map (so method calls can be chained).</returns>
  730. public BsonMemberMap MapField(string fieldName)
  731. {
  732. if (fieldName == null)
  733. {
  734. throw new ArgumentNullException("fieldName");
  735. }
  736. if (_frozen) { ThrowFrozenException(); }
  737. var fieldInfo = _classType.GetTypeInfo().GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  738. if (fieldInfo == null)
  739. {
  740. var message = string.Format("The class '{0}' does not have a field named '{1}'.", _classType.FullName, fieldName);
  741. throw new BsonSerializationException(message);
  742. }
  743. return MapMember(fieldInfo);
  744. }
  745. /// <summary>
  746. /// Creates a member map for the Id field and adds it to the class map.
  747. /// </summary>
  748. /// <param name="fieldName">The name of the Id field.</param>
  749. /// <returns>The member map (so method calls can be chained).</returns>
  750. public BsonMemberMap MapIdField(string fieldName)
  751. {
  752. if (fieldName == null)
  753. {
  754. throw new ArgumentNullException("fieldName");
  755. }
  756. if (_frozen) { ThrowFrozenException(); }
  757. var fieldMap = MapField(fieldName);
  758. SetIdMember(fieldMap);
  759. return fieldMap;
  760. }
  761. /// <summary>
  762. /// Creates a member map for the Id member and adds it to the class map.
  763. /// </summary>
  764. /// <param name="memberInfo">The member info for the Id member.</param>
  765. /// <returns>The member map (so method calls can be chained).</returns>
  766. public BsonMemberMap MapIdMember(MemberInfo memberInfo)
  767. {
  768. if (memberInfo == null)
  769. {
  770. throw new ArgumentNullException("memberInfo");
  771. }
  772. if (_frozen) { ThrowFrozenException(); }
  773. var memberMap = MapMember(memberInfo);
  774. SetIdMember(memberMap);
  775. return memberMap;
  776. }
  777. /// <summary>
  778. /// Creates a member map for the Id property and adds it to the class map.
  779. /// </summary>
  780. /// <param name="propertyName">The name of the Id property.</param>
  781. /// <returns>The member map (so method calls can be chained).</returns>
  782. public BsonMemberMap MapIdProperty(string propertyName)
  783. {
  784. if (propertyName == null)
  785. {
  786. throw new ArgumentNullException("propertyName");
  787. }
  788. if (_frozen) { ThrowFrozenException(); }
  789. var propertyMap = MapProperty(propertyName);
  790. SetIdMember(propertyMap);
  791. return propertyMap;
  792. }
  793. /// <summary>
  794. /// Creates a member map for a member and adds it to the class map.
  795. /// </summary>
  796. /// <param name="memberInfo">The member info.</param>
  797. /// <returns>The member map (so method calls can be chained).</returns>
  798. public BsonMemberMap MapMember(MemberInfo memberInfo)
  799. {
  800. if (memberInfo == null)
  801. {
  802. throw new ArgumentNullException("memberInfo");
  803. }
  804. if (!(memberInfo is FieldInfo) && !(memberInfo is PropertyInfo))
  805. {
  806. throw new ArgumentException("MemberInfo must be either a FieldInfo or a PropertyInfo.", "memberInfo");
  807. }
  808. EnsureMemberInfoIsForThisClass(memberInfo);
  809. if (_frozen) { ThrowFrozenException(); }
  810. var memberMap = _declaredMemberMaps.Find(m => m.MemberInfo == memberInfo);
  811. if (memberMap == null)
  812. {
  813. memberMap = new BsonMemberMap(this, memberInfo);
  814. _declaredMemberMaps.Add(memberMap);
  815. }
  816. return memberMap;
  817. }
  818. /// <summary>
  819. /// Creates a member map for a property and adds it to the class map.
  820. /// </summary>
  821. /// <param name="propertyName">The name of the property.</param>
  822. /// <returns>The member map (so method calls can be chained).</returns>
  823. public BsonMemberMap MapProperty(string propertyName)
  824. {
  825. if (propertyName == null)
  826. {
  827. throw new ArgumentNullException("propertyName");
  828. }
  829. if (_frozen) { ThrowFrozenException(); }
  830. var propertyInfo = _classType.GetTypeInfo().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  831. if (propertyInfo == null)
  832. {
  833. var message = string.Format("The class '{0}' does not have a property named '{1}'.", _classType.FullName, propertyName);
  834. throw new BsonSerializationException(message);
  835. }
  836. return MapMember(propertyInfo);
  837. }
  838. /// <summary>
  839. /// Resets the class map back to its initial state.
  840. /// </summary>
  841. public void Reset()
  842. {
  843. if (_frozen) { ThrowFrozenException(); }
  844. _creatorMaps.Clear();
  845. _creator = null;
  846. _declaredMemberMaps.Clear();
  847. _discriminator = _classType.Name;
  848. _discriminatorIsRequired = false;
  849. _extraElementsMemberMap = null;
  850. _idMemberMap = null;
  851. _ignoreExtraElements = true; // TODO: should this really be false?
  852. _ignoreExtraElementsIsInherited = false;
  853. _isRootClass = false;
  854. _knownTypes.Clear();
  855. }
  856. /// <summary>
  857. /// Sets the creator for the object.
  858. /// </summary>
  859. /// <param name="creator">The creator.</param>
  860. /// <returns>The class map (so method calls can be chained).</returns>
  861. public BsonClassMap SetCreator(Func<object> creator)
  862. {
  863. _creator = creator;
  864. return this;
  865. }
  866. /// <summary>
  867. /// Sets the discriminator.
  868. /// </summary>
  869. /// <param name="discriminator">The discriminator.</param>
  870. public void SetDiscriminator(string discriminator)
  871. {
  872. if (discriminator == null)
  873. {
  874. throw new ArgumentNullException("discriminator");
  875. }
  876. if (_frozen) { ThrowFrozenException(); }
  877. _discriminator = discriminator;
  878. }
  879. /// <summary>
  880. /// Sets whether a discriminator is required when serializing this class.
  881. /// </summary>
  882. /// <param name="discriminatorIsRequired">Whether a discriminator is required.</param>
  883. public void SetDiscriminatorIsRequired(bool discriminatorIsRequired)
  884. {
  885. if (_frozen) { ThrowFrozenException(); }
  886. _discriminatorIsRequired = discriminatorIsRequired;
  887. }
  888. /// <summary>
  889. /// Sets the member map of the member used to hold extra elements.
  890. /// </summary>
  891. /// <param name="memberMap">The extra elements member map.</param>
  892. public void SetExtraElementsMember(BsonMemberMap memberMap)
  893. {
  894. if (memberMap == null)
  895. {
  896. throw new ArgumentNullException("memberMap");
  897. }
  898. EnsureMemberMapIsForThisClass(memberMap);
  899. if (_frozen) { ThrowFrozenException(); }
  900. if (memberMap.MemberType != typeof(BsonDocument) && !typeof(IDictionary<string, object>).GetTypeInfo().IsAssignableFrom(memberMap.MemberType))
  901. {
  902. var message = string.Format("Type of ExtraElements member must be BsonDocument or implement IDictionary<string, object>.");
  903. throw new InvalidOperationException(message);
  904. }
  905. _extraElementsMemberMap = memberMap;
  906. }
  907. /// <summary>
  908. /// Adds a known type to the class map.
  909. /// </summary>
  910. /// <param name="type">The known type.</param>
  911. public void AddKnownType(Type type)
  912. {
  913. if (!_classType.GetTypeInfo().IsAssignableFrom(type))
  914. {
  915. string message = string.Format("Class {0} cannot be assigned to Class {1}. Ensure that known types are derived from the mapped class.", type.FullName, _classType.FullName);
  916. throw new ArgumentNullException("type", message);
  917. }
  918. if (_frozen) { ThrowFrozenException(); }
  919. _knownTypes.Add(type);
  920. }
  921. /// <summary>
  922. /// Sets the Id member.
  923. /// </summary>
  924. /// <param name="memberMap">The Id member (null if none).</param>
  925. public void SetIdMember(BsonMemberMap memberMap)
  926. {
  927. if (memberMap != null)
  928. {
  929. EnsureMemberMapIsForThisClass(memberMap);
  930. }
  931. if (_frozen) { ThrowFrozenException(); }
  932. _idMemberMap = memberMap;
  933. }
  934. /// <summary>
  935. /// Sets whether extra elements should be ignored when deserializing.
  936. /// </summary>
  937. /// <param name="ignoreExtraElements">Whether extra elements should be ignored when deserializing.</param>
  938. public void SetIgnoreExtraElements(bool ignoreExtraElements)
  939. {
  940. if (_frozen) { ThrowFrozenException(); }
  941. _ignoreExtraElements = ignoreExtraElements;
  942. }
  943. /// <summary>
  944. /// Sets whether the IgnoreExtraElements value should be inherited by derived classes.
  945. /// </summary>
  946. /// <param name="ignoreExtraElementsIsInherited">Whether the IgnoreExtraElements value should be inherited by derived classes.</param>
  947. public void SetIgnoreExtraElementsIsInherited(bool ignoreExtraElementsIsInherited)
  948. {
  949. if (_frozen) { ThrowFrozenException(); }
  950. _ignoreExtraElementsIsInherited = ignoreExtraElementsIsInherited;
  951. }
  952. /// <summary>
  953. /// Sets whether this class is a root class.
  954. /// </summary>
  955. /// <param name="isRootClass">Whether this class is a root class.</param>
  956. public void SetIsRootClass(bool isRootClass)
  957. {
  958. if (_frozen) { ThrowFrozenException(); }
  959. _isRootClass = isRootClass;
  960. }
  961. /// <summary>
  962. /// Removes a creator map for a constructor from the class map.
  963. /// </summary>
  964. /// <param name="constructorInfo">The constructor info.</param>
  965. public void UnmapConstructor(ConstructorInfo constructorInfo)
  966. {
  967. if (constructorInfo == null)
  968. {
  969. throw new ArgumentNullException("constructorInfo");
  970. }
  971. EnsureMemberInfoIsForThisClass(constructorInfo);
  972. if (_frozen) { ThrowFrozenException(); }
  973. var creatorMap = _creatorMaps.FirstOrDefault(m => m.MemberInfo == constructorInfo);
  974. if (creatorMap != null)
  975. {
  976. _creatorMaps.Remove(creatorMap);
  977. }
  978. }
  979. /// <summary>
  980. /// Removes a creator map for a factory method from the class map.
  981. /// </summary>
  982. /// <param name="methodInfo">The method info.</param>
  983. public void UnmapFactoryMethod(MethodInfo methodInfo)
  984. {
  985. if (methodInfo == null)
  986. {
  987. throw new ArgumentNullException("methodInfo");
  988. }
  989. EnsureMemberInfoIsForThisClass(methodInfo);
  990. if (_frozen) { ThrowFrozenException(); }
  991. var creatorMap = _creatorMaps.FirstOrDefault(m => m.MemberInfo == methodInfo);
  992. if (creatorMap != null)
  993. {
  994. _creatorMaps.Remove(creatorMap);
  995. }
  996. }
  997. /// <summary>
  998. /// Removes the member map for a field from the class map.
  999. /// </summary>
  1000. /// <param name="fieldName">The name of the field.</param>
  1001. public void UnmapField(string fieldName)
  1002. {
  1003. if (fieldName == null)
  1004. {
  1005. throw new ArgumentNullException("fieldName");
  1006. }
  1007. if (_frozen) { ThrowFrozenException(); }
  1008. var fieldInfo = _classType.GetTypeInfo().GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  1009. if (fieldInfo == null)
  1010. {
  1011. var message = string.Format("The class '{0}' does not have a field named '{1}'.", _classType.FullName, fieldName);
  1012. throw new BsonSerializationException(message);
  1013. }
  1014. UnmapMember(fieldInfo);
  1015. }
  1016. /// <summary>
  1017. /// Removes a member map from the class map.
  1018. /// </summary>
  1019. /// <param name="memberInfo">The member info.</param>
  1020. public void UnmapMember(MemberInfo memberInfo)
  1021. {
  1022. if (memberInfo == null)
  1023. {
  1024. throw new ArgumentNullException("memberInfo");
  1025. }
  1026. EnsureMemberInfoIsForThisClass(memberInfo);
  1027. if (_frozen) { ThrowFrozenException(); }
  1028. var memberMap = _declaredMemberMaps.Find(m => m.MemberInfo == memberInfo);
  1029. if (memberMap != null)
  1030. {
  1031. _declaredMemberMaps.Remove(memberMap);
  1032. if (_idMemberMap == memberMap)
  1033. {
  1034. _idMemberMap = null;
  1035. }
  1036. if (_extraElementsMemberMap == memberMap)
  1037. {
  1038. _extraElementsMemberMap = null;
  1039. }
  1040. }
  1041. }
  1042. /// <summary>
  1043. /// Removes the member map for a property from the class map.
  1044. /// </summary>
  1045. /// <param name="propertyName">The name of the property.</param>
  1046. public void UnmapProperty(string propertyName)
  1047. {
  1048. if (propertyName == null)
  1049. {
  1050. throw new ArgumentNullException("propertyName");
  1051. }
  1052. if (_frozen) { ThrowFrozenException(); }
  1053. var propertyInfo = _classType.GetTypeInfo().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  1054. if (propertyInfo == null)
  1055. {
  1056. var message = string.Format("The class '{0}' does not have a property named '{1}'.", _classType.FullName, propertyName);
  1057. throw new BsonSerializationException(message);
  1058. }
  1059. UnmapMember(propertyInfo);
  1060. }
  1061. // internal methods
  1062. /// <summary>
  1063. /// Gets the discriminator convention for the class.
  1064. /// </summary>
  1065. /// <returns>The discriminator convention for the class.</returns>
  1066. internal IDiscriminatorConvention GetDiscriminatorConvention()
  1067. {
  1068. // return a cached discriminator convention when possible
  1069. var discriminatorConvention = _discriminatorConvention;
  1070. if (discriminatorConvention == null)
  1071. {
  1072. // it's possible but harmless for multiple threads to do the initial lookup at the same time
  1073. discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(_classType);
  1074. _discriminatorConvention = discriminatorConvention;
  1075. }
  1076. return discriminatorConvention;
  1077. }
  1078. // private methods
  1079. private void AutoMapClass()
  1080. {
  1081. new ConventionRunner(_conventionPack).Apply(this);
  1082. OrderMembers();
  1083. foreach (var memberMap in _declaredMemberMaps)
  1084. {
  1085. TryFindShouldSerializeMethod(memberMap);
  1086. }
  1087. }
  1088. private void OrderMembers()
  1089. {
  1090. // only auto map properties declared in this class (and not in base classes)
  1091. var hasOrderedElements = false;
  1092. var hasUnorderedElements = false;
  1093. foreach (var memberMap in _declaredMemberMaps)
  1094. {
  1095. if (memberMap.Order != int.MaxValue)
  1096. {
  1097. hasOrderedElements |= true;
  1098. }
  1099. else
  1100. {
  1101. hasUnorderedElements |= true;
  1102. }
  1103. }
  1104. if (hasOrderedElements)
  1105. {
  1106. if (hasUnorderedElements)
  1107. {
  1108. // split out the unordered elements and add them back at the end (because Sort is unstable, see online help)
  1109. var unorderedElements = new List<BsonMemberMap>(_declaredMemberMaps.Where(pm => pm.Order == int.MaxValue));
  1110. _declaredMemberMaps.RemoveAll(m => m.Order == int.MaxValue);
  1111. _declaredMemberMaps.Sort((x, y) => x.Order.CompareTo(y.Order));
  1112. _declaredMemberMaps.AddRange(unorderedElements);
  1113. }
  1114. else
  1115. {
  1116. _declaredMemberMaps.Sort((x, y) => x.Order.CompareTo(y.Order));
  1117. }
  1118. }
  1119. }
  1120. private void TryFindShouldSerializeMethod(BsonMemberMap memberMap)
  1121. {
  1122. // see if the class has a method called ShouldSerializeXyz where Xyz is the name of this member
  1123. var shouldSerializeMethod = GetShouldSerializeMethod(memberMap.MemberInfo);
  1124. if (shouldSerializeMethod != null)
  1125. {
  1126. memberMap.SetShouldSerializeMethod(shouldSerializeMethod);
  1127. }
  1128. }
  1129. private void EnsureMemberInfoIsForThisClass(MemberInfo memberInfo)
  1130. {
  1131. if (memberInfo.DeclaringType != _classType)
  1132. {
  1133. var message = string.Format(
  1134. "The memberInfo argument must be for class {0}, but was for class {1}.",
  1135. _classType.Name,
  1136. memberInfo.DeclaringType.Name);
  1137. throw new ArgumentOutOfRangeException("memberInfo", message);
  1138. }
  1139. }
  1140. private void EnsureMemberMapIsForThisClass(BsonMemberMap memberMap)
  1141. {
  1142. if (memberMap.ClassMap != this)
  1143. {
  1144. var message = string.Format(
  1145. "The memberMap argument must be for class {0}, but was for class {1}.",
  1146. _classType.Name,
  1147. memberMap.ClassMap.ClassType.Name);
  1148. throw new ArgumentOutOfRangeException("memberMap", message);
  1149. }
  1150. }
  1151. private Func<object> GetCreator()
  1152. {
  1153. if (_creator == null)
  1154. {
  1155. Expression body;
  1156. var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  1157. var classTypeInfo = _classType.GetTypeInfo();
  1158. ConstructorInfo defaultConstructor = classTypeInfo.GetConstructors(bindingFlags)
  1159. .Where(c => c.GetParameters().Length == 0)
  1160. .SingleOrDefault();
  1161. #if ENABLE_IL2CPP
  1162. if (defaultConstructor != null)
  1163. {
  1164. _creator = () => defaultConstructor.Invoke(null);
  1165. }
  1166. else if(__getUninitializedObjectMethodInfo != null)
  1167. {
  1168. _creator = () => __getUninitializedObjectMethodInfo.Invoke(null, new object[] { this._classType });
  1169. }
  1170. else
  1171. {
  1172. var message = $"Type '{_classType.GetType().Name}' does not have a default constructor.";
  1173. throw new BsonSerializationException(message);
  1174. }
  1175. #else
  1176. if (defaultConstructor != null)
  1177. {
  1178. // lambdaExpression = () => (object) new TClass()
  1179. body = Expression.New(defaultConstructor);
  1180. }
  1181. else if (__getUninitializedObjectMethodInfo != null)
  1182. {
  1183. // lambdaExpression = () => FormatterServices.GetUninitializedObject(classType)
  1184. body = Expression.Call(__getUninitializedObjectMethodInfo, Expression.Constant(_classType));
  1185. }
  1186. else
  1187. {
  1188. var message = $"Type '{_classType.GetType().Name}' does not have a default constructor.";
  1189. throw new BsonSerializationException(message);
  1190. }
  1191. var lambdaExpression = Expression.Lambda<Func<object>>(body);
  1192. _creator = lambdaExpression.Compile();
  1193. #endif
  1194. }
  1195. return _creator;
  1196. }
  1197. private Func<object, bool> GetShouldSerializeMethod(MemberInfo memberInfo)
  1198. {
  1199. var shouldSerializeMethodName = "ShouldSerialize" + memberInfo.Name;
  1200. var shouldSerializeMethodInfo = _classType.GetTypeInfo().GetMethod(shouldSerializeMethodName, new Type[] { });
  1201. if (shouldSerializeMethodInfo != null &&
  1202. shouldSerializeMethodInfo.IsPublic &&
  1203. shouldSerializeMethodInfo.ReturnType == typeof(bool))
  1204. {
  1205. // lambdaExpression = (obj) => ((TClass) obj).ShouldSerializeXyz()
  1206. var objParameter = Expression.Parameter(typeof(object), "obj");
  1207. var lambdaExpression = Expression.Lambda<Func<object, bool>>(Expression.Call(Expression.Convert(objParameter, _classType), shouldSerializeMethodInfo), objParameter);
  1208. return lambdaExpression.Compile();
  1209. }
  1210. else
  1211. {
  1212. return null;
  1213. }
  1214. }
  1215. private bool IsAnonymousType(Type type)
  1216. {
  1217. // don't test for too many things in case implementation details change in the future
  1218. var typeInfo = type.GetTypeInfo();
  1219. return
  1220. typeInfo.GetCustomAttributes<CompilerGeneratedAttribute>(false).Any() &&
  1221. typeInfo.IsGenericType &&
  1222. type.Name.Contains("Anon"); // don't check for more than "Anon" so it works in mono also
  1223. }
  1224. private void ThrowFrozenException()
  1225. {
  1226. var message = string.Format("Class map for {0} has been frozen and no further changes are allowed.", _classType.FullName);
  1227. throw new InvalidOperationException(message);
  1228. }
  1229. private void ThrowNotFrozenException()
  1230. {
  1231. var message = string.Format("Class map for {0} has been not been frozen yet.", _classType.FullName);
  1232. throw new InvalidOperationException(message);
  1233. }
  1234. }
  1235. /// <summary>
  1236. /// Represents a mapping between a class and a BSON document.
  1237. /// </summary>
  1238. /// <typeparam name="TClass">The class.</typeparam>
  1239. public class BsonClassMap<TClass> : BsonClassMap
  1240. {
  1241. // constructors
  1242. /// <summary>
  1243. /// Initializes a new instance of the BsonClassMap class.
  1244. /// </summary>
  1245. public BsonClassMap()
  1246. : base(typeof(TClass))
  1247. {
  1248. }
  1249. /// <summary>
  1250. /// Initializes a new instance of the BsonClassMap class.
  1251. /// </summary>
  1252. /// <param name="classMapInitializer">The class map initializer.</param>
  1253. public BsonClassMap(Action<BsonClassMap<TClass>> classMapInitializer)
  1254. : base(typeof(TClass))
  1255. {
  1256. classMapInitializer(this);
  1257. }
  1258. // public methods
  1259. /// <summary>
  1260. /// Creates an instance.
  1261. /// </summary>
  1262. /// <returns>An instance.</returns>
  1263. public new TClass CreateInstance()
  1264. {
  1265. return (TClass)base.CreateInstance();
  1266. }
  1267. /// <summary>
  1268. /// Gets a member map.
  1269. /// </summary>
  1270. /// <typeparam name="TMember">The member type.</typeparam>
  1271. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1272. /// <returns>The member map.</returns>
  1273. public BsonMemberMap GetMemberMap<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1274. {
  1275. var memberName = GetMemberNameFromLambda(memberLambda);
  1276. return GetMemberMap(memberName);
  1277. }
  1278. /// <summary>
  1279. /// Creates a creator map and adds it to the class map.
  1280. /// </summary>
  1281. /// <param name="creatorLambda">Lambda expression specifying the creator code and parameters to use.</param>
  1282. /// <returns>The member map.</returns>
  1283. public BsonCreatorMap MapCreator(Expression<Func<TClass, TClass>> creatorLambda)
  1284. {
  1285. if (creatorLambda == null)
  1286. {
  1287. throw new ArgumentNullException("creatorLambda");
  1288. }
  1289. IEnumerable<MemberInfo> arguments;
  1290. var @delegate = new CreatorMapDelegateCompiler().CompileCreatorDelegate(creatorLambda, out arguments);
  1291. var creatorMap = MapCreator(@delegate);
  1292. creatorMap.SetArguments(arguments);
  1293. return creatorMap;
  1294. }
  1295. /// <summary>
  1296. /// Creates a member map for the extra elements field and adds it to the class map.
  1297. /// </summary>
  1298. /// <typeparam name="TMember">The member type.</typeparam>
  1299. /// <param name="fieldLambda">A lambda expression specifying the extra elements field.</param>
  1300. /// <returns>The member map.</returns>
  1301. public BsonMemberMap MapExtraElementsField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1302. {
  1303. var fieldMap = MapField(fieldLambda);
  1304. SetExtraElementsMember(fieldMap);
  1305. return fieldMap;
  1306. }
  1307. /// <summary>
  1308. /// Creates a member map for the extra elements member and adds it to the class map.
  1309. /// </summary>
  1310. /// <typeparam name="TMember">The member type.</typeparam>
  1311. /// <param name="memberLambda">A lambda expression specifying the extra elements member.</param>
  1312. /// <returns>The member map.</returns>
  1313. public BsonMemberMap MapExtraElementsMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1314. {
  1315. var memberMap = MapMember(memberLambda);
  1316. SetExtraElementsMember(memberMap);
  1317. return memberMap;
  1318. }
  1319. /// <summary>
  1320. /// Creates a member map for the extra elements property and adds it to the class map.
  1321. /// </summary>
  1322. /// <typeparam name="TMember">The member type.</typeparam>
  1323. /// <param name="propertyLambda">A lambda expression specifying the extra elements property.</param>
  1324. /// <returns>The member map.</returns>
  1325. public BsonMemberMap MapExtraElementsProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1326. {
  1327. var propertyMap = MapProperty(propertyLambda);
  1328. SetExtraElementsMember(propertyMap);
  1329. return propertyMap;
  1330. }
  1331. /// <summary>
  1332. /// Creates a member map for a field and adds it to the class map.
  1333. /// </summary>
  1334. /// <typeparam name="TMember">The member type.</typeparam>
  1335. /// <param name="fieldLambda">A lambda expression specifying the field.</param>
  1336. /// <returns>The member map.</returns>
  1337. public BsonMemberMap MapField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1338. {
  1339. return MapMember(fieldLambda);
  1340. }
  1341. /// <summary>
  1342. /// Creates a member map for the Id field and adds it to the class map.
  1343. /// </summary>
  1344. /// <typeparam name="TMember">The member type.</typeparam>
  1345. /// <param name="fieldLambda">A lambda expression specifying the Id field.</param>
  1346. /// <returns>The member map.</returns>
  1347. public BsonMemberMap MapIdField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1348. {
  1349. var fieldMap = MapField(fieldLambda);
  1350. SetIdMember(fieldMap);
  1351. return fieldMap;
  1352. }
  1353. /// <summary>
  1354. /// Creates a member map for the Id member and adds it to the class map.
  1355. /// </summary>
  1356. /// <typeparam name="TMember">The member type.</typeparam>
  1357. /// <param name="memberLambda">A lambda expression specifying the Id member.</param>
  1358. /// <returns>The member map.</returns>
  1359. public BsonMemberMap MapIdMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1360. {
  1361. var memberMap = MapMember(memberLambda);
  1362. SetIdMember(memberMap);
  1363. return memberMap;
  1364. }
  1365. /// <summary>
  1366. /// Creates a member map for the Id property and adds it to the class map.
  1367. /// </summary>
  1368. /// <typeparam name="TMember">The member type.</typeparam>
  1369. /// <param name="propertyLambda">A lambda expression specifying the Id property.</param>
  1370. /// <returns>The member map.</returns>
  1371. public BsonMemberMap MapIdProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1372. {
  1373. var propertyMap = MapProperty(propertyLambda);
  1374. SetIdMember(propertyMap);
  1375. return propertyMap;
  1376. }
  1377. /// <summary>
  1378. /// Creates a member map and adds it to the class map.
  1379. /// </summary>
  1380. /// <typeparam name="TMember">The member type.</typeparam>
  1381. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1382. /// <returns>The member map.</returns>
  1383. public BsonMemberMap MapMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1384. {
  1385. var memberInfo = GetMemberInfoFromLambda(memberLambda);
  1386. return MapMember(memberInfo);
  1387. }
  1388. /// <summary>
  1389. /// Creates a member map for the Id property and adds it to the class map.
  1390. /// </summary>
  1391. /// <typeparam name="TMember">The member type.</typeparam>
  1392. /// <param name="propertyLambda">A lambda expression specifying the Id property.</param>
  1393. /// <returns>The member map.</returns>
  1394. public BsonMemberMap MapProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1395. {
  1396. return MapMember(propertyLambda);
  1397. }
  1398. /// <summary>
  1399. /// Removes the member map for a field from the class map.
  1400. /// </summary>
  1401. /// <typeparam name="TMember">The member type.</typeparam>
  1402. /// <param name="fieldLambda">A lambda expression specifying the field.</param>
  1403. public void UnmapField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1404. {
  1405. UnmapMember(fieldLambda);
  1406. }
  1407. /// <summary>
  1408. /// Removes a member map from the class map.
  1409. /// </summary>
  1410. /// <typeparam name="TMember">The member type.</typeparam>
  1411. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1412. public void UnmapMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1413. {
  1414. var memberInfo = GetMemberInfoFromLambda(memberLambda);
  1415. UnmapMember(memberInfo);
  1416. }
  1417. /// <summary>
  1418. /// Removes a member map for a property from the class map.
  1419. /// </summary>
  1420. /// <typeparam name="TMember">The member type.</typeparam>
  1421. /// <param name="propertyLambda">A lambda expression specifying the property.</param>
  1422. public void UnmapProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1423. {
  1424. UnmapMember(propertyLambda);
  1425. }
  1426. // private static methods
  1427. private static MethodInfo[] GetPropertyAccessors(PropertyInfo propertyInfo)
  1428. {
  1429. return propertyInfo.GetAccessors(true);
  1430. }
  1431. private static MemberInfo GetMemberInfoFromLambda<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1432. {
  1433. var body = memberLambda.Body;
  1434. MemberExpression memberExpression;
  1435. switch (body.NodeType)
  1436. {
  1437. case ExpressionType.MemberAccess:
  1438. memberExpression = (MemberExpression)body;
  1439. break;
  1440. case ExpressionType.Convert:
  1441. var convertExpression = (UnaryExpression)body;
  1442. memberExpression = (MemberExpression)convertExpression.Operand;
  1443. break;
  1444. default:
  1445. throw new BsonSerializationException("Invalid lambda expression");
  1446. }
  1447. var memberInfo = memberExpression.Member;
  1448. if (memberInfo is PropertyInfo)
  1449. {
  1450. if (memberInfo.DeclaringType.GetTypeInfo().IsInterface)
  1451. {
  1452. memberInfo = FindPropertyImplementation((PropertyInfo)memberInfo, typeof(TClass));
  1453. }
  1454. }
  1455. else if (!(memberInfo is FieldInfo))
  1456. {
  1457. throw new BsonSerializationException("Invalid lambda expression");
  1458. }
  1459. return memberInfo;
  1460. }
  1461. private static string GetMemberNameFromLambda<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1462. {
  1463. return GetMemberInfoFromLambda(memberLambda).Name;
  1464. }
  1465. private static PropertyInfo FindPropertyImplementation(PropertyInfo interfacePropertyInfo, Type actualType)
  1466. {
  1467. var interfaceType = interfacePropertyInfo.DeclaringType;
  1468. #if NETSTANDARD1_5 || NETSTANDARD1_6
  1469. var actualTypeInfo = actualType.GetTypeInfo();
  1470. var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
  1471. var actualTypePropertyInfos = actualTypeInfo.GetMembers(bindingFlags).OfType<PropertyInfo>();
  1472. var explicitlyImplementedPropertyName = $"{interfacePropertyInfo.DeclaringType.FullName}.{interfacePropertyInfo.Name}".Replace("+", ".");
  1473. var explicitlyImplementedPropertyInfo = actualTypePropertyInfos
  1474. .Where(p => p.Name == explicitlyImplementedPropertyName)
  1475. .SingleOrDefault();
  1476. if (explicitlyImplementedPropertyInfo != null)
  1477. {
  1478. return explicitlyImplementedPropertyInfo;
  1479. }
  1480. var implicitlyImplementedPropertyInfo = actualTypePropertyInfos
  1481. .Where(p => p.Name == interfacePropertyInfo.Name && p.PropertyType == interfacePropertyInfo.PropertyType)
  1482. .SingleOrDefault();
  1483. if (implicitlyImplementedPropertyInfo != null)
  1484. {
  1485. return implicitlyImplementedPropertyInfo;
  1486. }
  1487. throw new BsonSerializationException($"Unable to find property info for property: '{interfacePropertyInfo.Name}'.");
  1488. #else
  1489. // An interface map must be used because because there is no
  1490. // other officially documented way to derive the explicitly
  1491. // implemented property name.
  1492. var interfaceMap = actualType.GetInterfaceMap(interfaceType);
  1493. var interfacePropertyAccessors = GetPropertyAccessors(interfacePropertyInfo);
  1494. var actualPropertyAccessors = interfacePropertyAccessors.Select(interfacePropertyAccessor =>
  1495. {
  1496. var index = Array.IndexOf<MethodInfo>(interfaceMap.InterfaceMethods, interfacePropertyAccessor);
  1497. return interfaceMap.TargetMethods[index];
  1498. });
  1499. // Binding must be done by accessor methods because interface
  1500. // maps only map accessor methods and do not map properties.
  1501. return actualType.GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
  1502. .Single(propertyInfo =>
  1503. {
  1504. // we are looking for a property that implements all the required accessors
  1505. var propertyAccessors = GetPropertyAccessors(propertyInfo);
  1506. return actualPropertyAccessors.All(x => propertyAccessors.Contains(x));
  1507. });
  1508. #endif
  1509. }
  1510. }
  1511. }