BsonClassMap.cs 64 KB

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