BsonClassMap.cs 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649
  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. var defaultConstructor = classTypeInfo.GetConstructors(bindingFlags)
  1158. .Where(c => c.GetParameters().Length == 0)
  1159. .SingleOrDefault();
  1160. if (defaultConstructor != null)
  1161. {
  1162. // lambdaExpression = () => (object) new TClass()
  1163. body = Expression.New(defaultConstructor);
  1164. }
  1165. else if (__getUninitializedObjectMethodInfo != null)
  1166. {
  1167. // lambdaExpression = () => FormatterServices.GetUninitializedObject(classType)
  1168. body = Expression.Call(__getUninitializedObjectMethodInfo, Expression.Constant(_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. var lambdaExpression = Expression.Lambda<Func<object>>(body);
  1176. _creator = lambdaExpression.Compile();
  1177. }
  1178. return _creator;
  1179. }
  1180. private Func<object, bool> GetShouldSerializeMethod(MemberInfo memberInfo)
  1181. {
  1182. var shouldSerializeMethodName = "ShouldSerialize" + memberInfo.Name;
  1183. var shouldSerializeMethodInfo = _classType.GetTypeInfo().GetMethod(shouldSerializeMethodName, new Type[] { });
  1184. if (shouldSerializeMethodInfo != null &&
  1185. shouldSerializeMethodInfo.IsPublic &&
  1186. shouldSerializeMethodInfo.ReturnType == typeof(bool))
  1187. {
  1188. // lambdaExpression = (obj) => ((TClass) obj).ShouldSerializeXyz()
  1189. var objParameter = Expression.Parameter(typeof(object), "obj");
  1190. var lambdaExpression = Expression.Lambda<Func<object, bool>>(Expression.Call(Expression.Convert(objParameter, _classType), shouldSerializeMethodInfo), objParameter);
  1191. return lambdaExpression.Compile();
  1192. }
  1193. else
  1194. {
  1195. return null;
  1196. }
  1197. }
  1198. private bool IsAnonymousType(Type type)
  1199. {
  1200. // don't test for too many things in case implementation details change in the future
  1201. var typeInfo = type.GetTypeInfo();
  1202. return
  1203. typeInfo.GetCustomAttributes<CompilerGeneratedAttribute>(false).Any() &&
  1204. typeInfo.IsGenericType &&
  1205. type.Name.Contains("Anon"); // don't check for more than "Anon" so it works in mono also
  1206. }
  1207. private void ThrowFrozenException()
  1208. {
  1209. var message = string.Format("Class map for {0} has been frozen and no further changes are allowed.", _classType.FullName);
  1210. throw new InvalidOperationException(message);
  1211. }
  1212. private void ThrowNotFrozenException()
  1213. {
  1214. var message = string.Format("Class map for {0} has been not been frozen yet.", _classType.FullName);
  1215. throw new InvalidOperationException(message);
  1216. }
  1217. }
  1218. /// <summary>
  1219. /// Represents a mapping between a class and a BSON document.
  1220. /// </summary>
  1221. /// <typeparam name="TClass">The class.</typeparam>
  1222. public class BsonClassMap<TClass> : BsonClassMap
  1223. {
  1224. // constructors
  1225. /// <summary>
  1226. /// Initializes a new instance of the BsonClassMap class.
  1227. /// </summary>
  1228. public BsonClassMap()
  1229. : base(typeof(TClass))
  1230. {
  1231. }
  1232. /// <summary>
  1233. /// Initializes a new instance of the BsonClassMap class.
  1234. /// </summary>
  1235. /// <param name="classMapInitializer">The class map initializer.</param>
  1236. public BsonClassMap(Action<BsonClassMap<TClass>> classMapInitializer)
  1237. : base(typeof(TClass))
  1238. {
  1239. classMapInitializer(this);
  1240. }
  1241. // public methods
  1242. /// <summary>
  1243. /// Creates an instance.
  1244. /// </summary>
  1245. /// <returns>An instance.</returns>
  1246. public new TClass CreateInstance()
  1247. {
  1248. return (TClass)base.CreateInstance();
  1249. }
  1250. /// <summary>
  1251. /// Gets a member map.
  1252. /// </summary>
  1253. /// <typeparam name="TMember">The member type.</typeparam>
  1254. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1255. /// <returns>The member map.</returns>
  1256. public BsonMemberMap GetMemberMap<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1257. {
  1258. var memberName = GetMemberNameFromLambda(memberLambda);
  1259. return GetMemberMap(memberName);
  1260. }
  1261. /// <summary>
  1262. /// Creates a creator map and adds it to the class map.
  1263. /// </summary>
  1264. /// <param name="creatorLambda">Lambda expression specifying the creator code and parameters to use.</param>
  1265. /// <returns>The member map.</returns>
  1266. public BsonCreatorMap MapCreator(Expression<Func<TClass, TClass>> creatorLambda)
  1267. {
  1268. if (creatorLambda == null)
  1269. {
  1270. throw new ArgumentNullException("creatorLambda");
  1271. }
  1272. IEnumerable<MemberInfo> arguments;
  1273. var @delegate = new CreatorMapDelegateCompiler().CompileCreatorDelegate(creatorLambda, out arguments);
  1274. var creatorMap = MapCreator(@delegate);
  1275. creatorMap.SetArguments(arguments);
  1276. return creatorMap;
  1277. }
  1278. /// <summary>
  1279. /// Creates a member map for the extra elements field and adds it to the class map.
  1280. /// </summary>
  1281. /// <typeparam name="TMember">The member type.</typeparam>
  1282. /// <param name="fieldLambda">A lambda expression specifying the extra elements field.</param>
  1283. /// <returns>The member map.</returns>
  1284. public BsonMemberMap MapExtraElementsField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1285. {
  1286. var fieldMap = MapField(fieldLambda);
  1287. SetExtraElementsMember(fieldMap);
  1288. return fieldMap;
  1289. }
  1290. /// <summary>
  1291. /// Creates a member map for the extra elements member and adds it to the class map.
  1292. /// </summary>
  1293. /// <typeparam name="TMember">The member type.</typeparam>
  1294. /// <param name="memberLambda">A lambda expression specifying the extra elements member.</param>
  1295. /// <returns>The member map.</returns>
  1296. public BsonMemberMap MapExtraElementsMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1297. {
  1298. var memberMap = MapMember(memberLambda);
  1299. SetExtraElementsMember(memberMap);
  1300. return memberMap;
  1301. }
  1302. /// <summary>
  1303. /// Creates a member map for the extra elements property and adds it to the class map.
  1304. /// </summary>
  1305. /// <typeparam name="TMember">The member type.</typeparam>
  1306. /// <param name="propertyLambda">A lambda expression specifying the extra elements property.</param>
  1307. /// <returns>The member map.</returns>
  1308. public BsonMemberMap MapExtraElementsProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1309. {
  1310. var propertyMap = MapProperty(propertyLambda);
  1311. SetExtraElementsMember(propertyMap);
  1312. return propertyMap;
  1313. }
  1314. /// <summary>
  1315. /// Creates a member map for a field and adds it to the class map.
  1316. /// </summary>
  1317. /// <typeparam name="TMember">The member type.</typeparam>
  1318. /// <param name="fieldLambda">A lambda expression specifying the field.</param>
  1319. /// <returns>The member map.</returns>
  1320. public BsonMemberMap MapField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1321. {
  1322. return MapMember(fieldLambda);
  1323. }
  1324. /// <summary>
  1325. /// Creates a member map for the Id field and adds it to the class map.
  1326. /// </summary>
  1327. /// <typeparam name="TMember">The member type.</typeparam>
  1328. /// <param name="fieldLambda">A lambda expression specifying the Id field.</param>
  1329. /// <returns>The member map.</returns>
  1330. public BsonMemberMap MapIdField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1331. {
  1332. var fieldMap = MapField(fieldLambda);
  1333. SetIdMember(fieldMap);
  1334. return fieldMap;
  1335. }
  1336. /// <summary>
  1337. /// Creates a member map for the Id member and adds it to the class map.
  1338. /// </summary>
  1339. /// <typeparam name="TMember">The member type.</typeparam>
  1340. /// <param name="memberLambda">A lambda expression specifying the Id member.</param>
  1341. /// <returns>The member map.</returns>
  1342. public BsonMemberMap MapIdMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1343. {
  1344. var memberMap = MapMember(memberLambda);
  1345. SetIdMember(memberMap);
  1346. return memberMap;
  1347. }
  1348. /// <summary>
  1349. /// Creates a member map for the Id property and adds it to the class map.
  1350. /// </summary>
  1351. /// <typeparam name="TMember">The member type.</typeparam>
  1352. /// <param name="propertyLambda">A lambda expression specifying the Id property.</param>
  1353. /// <returns>The member map.</returns>
  1354. public BsonMemberMap MapIdProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1355. {
  1356. var propertyMap = MapProperty(propertyLambda);
  1357. SetIdMember(propertyMap);
  1358. return propertyMap;
  1359. }
  1360. /// <summary>
  1361. /// Creates a member map and adds it to the class map.
  1362. /// </summary>
  1363. /// <typeparam name="TMember">The member type.</typeparam>
  1364. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1365. /// <returns>The member map.</returns>
  1366. public BsonMemberMap MapMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1367. {
  1368. var memberInfo = GetMemberInfoFromLambda(memberLambda);
  1369. return MapMember(memberInfo);
  1370. }
  1371. /// <summary>
  1372. /// Creates a member map for the Id property and adds it to the class map.
  1373. /// </summary>
  1374. /// <typeparam name="TMember">The member type.</typeparam>
  1375. /// <param name="propertyLambda">A lambda expression specifying the Id property.</param>
  1376. /// <returns>The member map.</returns>
  1377. public BsonMemberMap MapProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1378. {
  1379. return MapMember(propertyLambda);
  1380. }
  1381. /// <summary>
  1382. /// Removes the member map for a field from the class map.
  1383. /// </summary>
  1384. /// <typeparam name="TMember">The member type.</typeparam>
  1385. /// <param name="fieldLambda">A lambda expression specifying the field.</param>
  1386. public void UnmapField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1387. {
  1388. UnmapMember(fieldLambda);
  1389. }
  1390. /// <summary>
  1391. /// Removes a member map from the class map.
  1392. /// </summary>
  1393. /// <typeparam name="TMember">The member type.</typeparam>
  1394. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1395. public void UnmapMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1396. {
  1397. var memberInfo = GetMemberInfoFromLambda(memberLambda);
  1398. UnmapMember(memberInfo);
  1399. }
  1400. /// <summary>
  1401. /// Removes a member map for a property from the class map.
  1402. /// </summary>
  1403. /// <typeparam name="TMember">The member type.</typeparam>
  1404. /// <param name="propertyLambda">A lambda expression specifying the property.</param>
  1405. public void UnmapProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1406. {
  1407. UnmapMember(propertyLambda);
  1408. }
  1409. // private static methods
  1410. private static MethodInfo[] GetPropertyAccessors(PropertyInfo propertyInfo)
  1411. {
  1412. return propertyInfo.GetAccessors(true);
  1413. }
  1414. private static MemberInfo GetMemberInfoFromLambda<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1415. {
  1416. var body = memberLambda.Body;
  1417. MemberExpression memberExpression;
  1418. switch (body.NodeType)
  1419. {
  1420. case ExpressionType.MemberAccess:
  1421. memberExpression = (MemberExpression)body;
  1422. break;
  1423. case ExpressionType.Convert:
  1424. var convertExpression = (UnaryExpression)body;
  1425. memberExpression = (MemberExpression)convertExpression.Operand;
  1426. break;
  1427. default:
  1428. throw new BsonSerializationException("Invalid lambda expression");
  1429. }
  1430. var memberInfo = memberExpression.Member;
  1431. if (memberInfo is PropertyInfo)
  1432. {
  1433. if (memberInfo.DeclaringType.GetTypeInfo().IsInterface)
  1434. {
  1435. memberInfo = FindPropertyImplementation((PropertyInfo)memberInfo, typeof(TClass));
  1436. }
  1437. }
  1438. else if (!(memberInfo is FieldInfo))
  1439. {
  1440. throw new BsonSerializationException("Invalid lambda expression");
  1441. }
  1442. return memberInfo;
  1443. }
  1444. private static string GetMemberNameFromLambda<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1445. {
  1446. return GetMemberInfoFromLambda(memberLambda).Name;
  1447. }
  1448. private static PropertyInfo FindPropertyImplementation(PropertyInfo interfacePropertyInfo, Type actualType)
  1449. {
  1450. var interfaceType = interfacePropertyInfo.DeclaringType;
  1451. #if NETSTANDARD1_5 || NETSTANDARD1_6
  1452. var actualTypeInfo = actualType.GetTypeInfo();
  1453. var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
  1454. var actualTypePropertyInfos = actualTypeInfo.GetMembers(bindingFlags).OfType<PropertyInfo>();
  1455. var explicitlyImplementedPropertyName = $"{interfacePropertyInfo.DeclaringType.FullName}.{interfacePropertyInfo.Name}".Replace("+", ".");
  1456. var explicitlyImplementedPropertyInfo = actualTypePropertyInfos
  1457. .Where(p => p.Name == explicitlyImplementedPropertyName)
  1458. .SingleOrDefault();
  1459. if (explicitlyImplementedPropertyInfo != null)
  1460. {
  1461. return explicitlyImplementedPropertyInfo;
  1462. }
  1463. var implicitlyImplementedPropertyInfo = actualTypePropertyInfos
  1464. .Where(p => p.Name == interfacePropertyInfo.Name && p.PropertyType == interfacePropertyInfo.PropertyType)
  1465. .SingleOrDefault();
  1466. if (implicitlyImplementedPropertyInfo != null)
  1467. {
  1468. return implicitlyImplementedPropertyInfo;
  1469. }
  1470. throw new BsonSerializationException($"Unable to find property info for property: '{interfacePropertyInfo.Name}'.");
  1471. #else
  1472. // An interface map must be used because because there is no
  1473. // other officially documented way to derive the explicitly
  1474. // implemented property name.
  1475. var interfaceMap = actualType.GetInterfaceMap(interfaceType);
  1476. var interfacePropertyAccessors = GetPropertyAccessors(interfacePropertyInfo);
  1477. var actualPropertyAccessors = interfacePropertyAccessors.Select(interfacePropertyAccessor =>
  1478. {
  1479. var index = Array.IndexOf<MethodInfo>(interfaceMap.InterfaceMethods, interfacePropertyAccessor);
  1480. return interfaceMap.TargetMethods[index];
  1481. });
  1482. // Binding must be done by accessor methods because interface
  1483. // maps only map accessor methods and do not map properties.
  1484. return actualType.GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
  1485. .Single(propertyInfo =>
  1486. {
  1487. // we are looking for a property that implements all the required accessors
  1488. var propertyAccessors = GetPropertyAccessors(propertyInfo);
  1489. return actualPropertyAccessors.All(x => propertyAccessors.Contains(x));
  1490. });
  1491. #endif
  1492. }
  1493. }
  1494. }