BsonClassMap.cs 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616
  1. /* Copyright 2010-present 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 NET452
  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 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. _baseClassMap.Freeze();
  439. _discriminatorIsRequired |= _baseClassMap._discriminatorIsRequired;
  440. _hasRootClass |= (_isRootClass || _baseClassMap.HasRootClass);
  441. _allMemberMaps.AddRange(_baseClassMap.AllMemberMaps);
  442. if (_baseClassMap.IgnoreExtraElements && _baseClassMap.IgnoreExtraElementsIsInherited)
  443. {
  444. _ignoreExtraElements = true;
  445. _ignoreExtraElementsIsInherited = true;
  446. }
  447. }
  448. _declaredMemberMaps = _declaredMemberMaps.OrderBy(m => m.Order).ToList(); // we're counting on OrderBy being a stable sort
  449. _allMemberMaps.AddRange(_declaredMemberMaps);
  450. if (_idMemberMap == null)
  451. {
  452. // see if we can inherit the idMemberMap from our base class
  453. if (_baseClassMap != null)
  454. {
  455. _idMemberMap = _baseClassMap.IdMemberMap;
  456. }
  457. }
  458. else
  459. {
  460. if (_idMemberMap.ClassMap == this)
  461. {
  462. // conventions could have set this to an improper value
  463. _idMemberMap.SetElementName("_id");
  464. }
  465. }
  466. if (_extraElementsMemberMap == null)
  467. {
  468. // see if we can inherit the extraElementsMemberMap from our base class
  469. if (_baseClassMap != null)
  470. {
  471. _extraElementsMemberMap = _baseClassMap.ExtraElementsMemberMap;
  472. }
  473. }
  474. _extraElementsMemberIndex = -1;
  475. for (int memberIndex = 0; memberIndex < _allMemberMaps.Count; ++memberIndex)
  476. {
  477. var memberMap = _allMemberMaps[memberIndex];
  478. int conflictingMemberIndex;
  479. if (!_elementTrie.TryGetValue(memberMap.ElementName, out conflictingMemberIndex))
  480. {
  481. _elementTrie.Add(memberMap.ElementName, memberIndex);
  482. }
  483. else
  484. {
  485. var conflictingMemberMap = _allMemberMaps[conflictingMemberIndex];
  486. var fieldOrProperty = (memberMap.MemberInfo is FieldInfo) ? "field" : "property";
  487. var conflictingFieldOrProperty = (conflictingMemberMap.MemberInfo is FieldInfo) ? "field" : "property";
  488. var conflictingType = conflictingMemberMap.MemberInfo.DeclaringType;
  489. string message;
  490. if (conflictingType == _classType)
  491. {
  492. message = string.Format(
  493. "The {0} '{1}' of type '{2}' cannot use element name '{3}' because it is already being used by {4} '{5}'.",
  494. fieldOrProperty, memberMap.MemberName, _classType.FullName, memberMap.ElementName, conflictingFieldOrProperty, conflictingMemberMap.MemberName);
  495. }
  496. else
  497. {
  498. message = string.Format(
  499. "The {0} '{1}' of type '{2}' cannot use element name '{3}' because it is already being used by {4} '{5}' of type '{6}'.",
  500. fieldOrProperty, memberMap.MemberName, _classType.FullName, memberMap.ElementName, conflictingFieldOrProperty, conflictingMemberMap.MemberName, conflictingType.FullName);
  501. }
  502. throw new BsonSerializationException(message);
  503. }
  504. if (memberMap == _extraElementsMemberMap)
  505. {
  506. _extraElementsMemberIndex = memberIndex;
  507. }
  508. }
  509. // mark this classMap frozen before we start working on knownTypes
  510. // because we might get back to this same classMap while processing knownTypes
  511. foreach (var creatorMap in _creatorMaps)
  512. {
  513. creatorMap.Freeze();
  514. }
  515. foreach (var memberMap in _declaredMemberMaps)
  516. {
  517. memberMap.Freeze();
  518. }
  519. _frozen = true;
  520. // use a queue to postpone processing of known types until we get back to the first level call to Freeze
  521. // this avoids infinite recursion when going back down the inheritance tree while processing known types
  522. foreach (var knownType in _knownTypes)
  523. {
  524. __knownTypesQueue.Enqueue(knownType);
  525. }
  526. // if we are back to the first level go ahead and process any queued known types
  527. if (__freezeNestingLevel == 1)
  528. {
  529. while (__knownTypesQueue.Count != 0)
  530. {
  531. var knownType = __knownTypesQueue.Dequeue();
  532. LookupClassMap(knownType); // will AutoMap and/or Freeze knownType if necessary
  533. }
  534. }
  535. }
  536. finally
  537. {
  538. __freezeNestingLevel--;
  539. }
  540. }
  541. }
  542. finally
  543. {
  544. BsonSerializer.ConfigLock.ExitWriteLock();
  545. }
  546. return this;
  547. }
  548. /// <summary>
  549. /// Gets a member map (only considers members declared in this class).
  550. /// </summary>
  551. /// <param name="memberName">The member name.</param>
  552. /// <returns>The member map (or null if the member was not found).</returns>
  553. public BsonMemberMap GetMemberMap(string memberName)
  554. {
  555. if (memberName == null)
  556. {
  557. throw new ArgumentNullException("memberName");
  558. }
  559. // can be called whether frozen or not
  560. return _declaredMemberMaps.Find(m => m.MemberName == memberName);
  561. }
  562. /// <summary>
  563. /// Gets the member map for a BSON element.
  564. /// </summary>
  565. /// <param name="elementName">The name of the element.</param>
  566. /// <returns>The member map.</returns>
  567. public BsonMemberMap GetMemberMapForElement(string elementName)
  568. {
  569. if (elementName == null)
  570. {
  571. throw new ArgumentNullException("elementName");
  572. }
  573. if (!_frozen) { ThrowNotFrozenException(); }
  574. int memberIndex;
  575. if (!_elementTrie.TryGetValue(elementName, out memberIndex))
  576. {
  577. return null;
  578. }
  579. var memberMap = _allMemberMaps[memberIndex];
  580. return memberMap;
  581. }
  582. /// <summary>
  583. /// Creates a creator map for a constructor and adds it to the class map.
  584. /// </summary>
  585. /// <param name="constructorInfo">The constructor info.</param>
  586. /// <returns>The creator map (so method calls can be chained).</returns>
  587. public BsonCreatorMap MapConstructor(ConstructorInfo constructorInfo)
  588. {
  589. if (constructorInfo == null)
  590. {
  591. throw new ArgumentNullException("constructorInfo");
  592. }
  593. EnsureMemberInfoIsForThisClass(constructorInfo);
  594. if (_frozen) { ThrowFrozenException(); }
  595. var creatorMap = _creatorMaps.FirstOrDefault(m => m.MemberInfo == constructorInfo);
  596. if (creatorMap == null)
  597. {
  598. var @delegate = new CreatorMapDelegateCompiler().CompileConstructorDelegate(constructorInfo);
  599. creatorMap = new BsonCreatorMap(this, constructorInfo, @delegate);
  600. _creatorMaps.Add(creatorMap);
  601. }
  602. return creatorMap;
  603. }
  604. /// <summary>
  605. /// Creates a creator map for a constructor and adds it to the class map.
  606. /// </summary>
  607. /// <param name="constructorInfo">The constructor info.</param>
  608. /// <param name="argumentNames">The argument names.</param>
  609. /// <returns>The creator map (so method calls can be chained).</returns>
  610. public BsonCreatorMap MapConstructor(ConstructorInfo constructorInfo, params string[] argumentNames)
  611. {
  612. var creatorMap = MapConstructor(constructorInfo);
  613. creatorMap.SetArguments(argumentNames);
  614. return creatorMap;
  615. }
  616. /// <summary>
  617. /// Creates a creator map and adds it to the class.
  618. /// </summary>
  619. /// <param name="delegate">The delegate.</param>
  620. /// <returns>The factory method map (so method calls can be chained).</returns>
  621. public BsonCreatorMap MapCreator(Delegate @delegate)
  622. {
  623. if (@delegate == null)
  624. {
  625. throw new ArgumentNullException("delegate");
  626. }
  627. if (_frozen) { ThrowFrozenException(); }
  628. var creatorMap = new BsonCreatorMap(this, null, @delegate);
  629. _creatorMaps.Add(creatorMap);
  630. return creatorMap;
  631. }
  632. /// <summary>
  633. /// Creates a creator map and adds it to the class.
  634. /// </summary>
  635. /// <param name="delegate">The delegate.</param>
  636. /// <param name="argumentNames">The argument names.</param>
  637. /// <returns>The factory method map (so method calls can be chained).</returns>
  638. public BsonCreatorMap MapCreator(Delegate @delegate, params string[] argumentNames)
  639. {
  640. var creatorMap = MapCreator(@delegate);
  641. creatorMap.SetArguments(argumentNames);
  642. return creatorMap;
  643. }
  644. /// <summary>
  645. /// Creates a member map for the extra elements field and adds it to the class map.
  646. /// </summary>
  647. /// <param name="fieldName">The name of the extra elements field.</param>
  648. /// <returns>The member map (so method calls can be chained).</returns>
  649. public BsonMemberMap MapExtraElementsField(string fieldName)
  650. {
  651. if (fieldName == null)
  652. {
  653. throw new ArgumentNullException("fieldName");
  654. }
  655. if (_frozen) { ThrowFrozenException(); }
  656. var fieldMap = MapField(fieldName);
  657. SetExtraElementsMember(fieldMap);
  658. return fieldMap;
  659. }
  660. /// <summary>
  661. /// Creates a member map for the extra elements member and adds it to the class map.
  662. /// </summary>
  663. /// <param name="memberInfo">The member info for the extra elements member.</param>
  664. /// <returns>The member map (so method calls can be chained).</returns>
  665. public BsonMemberMap MapExtraElementsMember(MemberInfo memberInfo)
  666. {
  667. if (memberInfo == null)
  668. {
  669. throw new ArgumentNullException("memberInfo");
  670. }
  671. if (_frozen) { ThrowFrozenException(); }
  672. var memberMap = MapMember(memberInfo);
  673. SetExtraElementsMember(memberMap);
  674. return memberMap;
  675. }
  676. /// <summary>
  677. /// Creates a member map for the extra elements property and adds it to the class map.
  678. /// </summary>
  679. /// <param name="propertyName">The name of the property.</param>
  680. /// <returns>The member map (so method calls can be chained).</returns>
  681. public BsonMemberMap MapExtraElementsProperty(string propertyName)
  682. {
  683. if (propertyName == null)
  684. {
  685. throw new ArgumentNullException("propertyName");
  686. }
  687. if (_frozen) { ThrowFrozenException(); }
  688. var propertyMap = MapProperty(propertyName);
  689. SetExtraElementsMember(propertyMap);
  690. return propertyMap;
  691. }
  692. /// <summary>
  693. /// Creates a creator map for a factory method and adds it to the class.
  694. /// </summary>
  695. /// <param name="methodInfo">The method info.</param>
  696. /// <returns>The creator map (so method calls can be chained).</returns>
  697. public BsonCreatorMap MapFactoryMethod(MethodInfo methodInfo)
  698. {
  699. if (methodInfo == null)
  700. {
  701. throw new ArgumentNullException("methodInfo");
  702. }
  703. EnsureMemberInfoIsForThisClass(methodInfo);
  704. if (_frozen) { ThrowFrozenException(); }
  705. var creatorMap = _creatorMaps.FirstOrDefault(m => m.MemberInfo == methodInfo);
  706. if (creatorMap == null)
  707. {
  708. var @delegate = new CreatorMapDelegateCompiler().CompileFactoryMethodDelegate(methodInfo);
  709. creatorMap = new BsonCreatorMap(this, methodInfo, @delegate);
  710. _creatorMaps.Add(creatorMap);
  711. }
  712. return creatorMap;
  713. }
  714. /// <summary>
  715. /// Creates a creator map for a factory method and adds it to the class.
  716. /// </summary>
  717. /// <param name="methodInfo">The method info.</param>
  718. /// <param name="argumentNames">The argument names.</param>
  719. /// <returns>The creator map (so method calls can be chained).</returns>
  720. public BsonCreatorMap MapFactoryMethod(MethodInfo methodInfo, params string[] argumentNames)
  721. {
  722. var creatorMap = MapFactoryMethod(methodInfo);
  723. creatorMap.SetArguments(argumentNames);
  724. return creatorMap;
  725. }
  726. /// <summary>
  727. /// Creates a member map for a field and adds it to the class map.
  728. /// </summary>
  729. /// <param name="fieldName">The name of the field.</param>
  730. /// <returns>The member map (so method calls can be chained).</returns>
  731. public BsonMemberMap MapField(string fieldName)
  732. {
  733. if (fieldName == null)
  734. {
  735. throw new ArgumentNullException("fieldName");
  736. }
  737. if (_frozen) { ThrowFrozenException(); }
  738. var fieldInfo = _classType.GetTypeInfo().GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  739. if (fieldInfo == null)
  740. {
  741. var message = string.Format("The class '{0}' does not have a field named '{1}'.", _classType.FullName, fieldName);
  742. throw new BsonSerializationException(message);
  743. }
  744. return MapMember(fieldInfo);
  745. }
  746. /// <summary>
  747. /// Creates a member map for the Id field and adds it to the class map.
  748. /// </summary>
  749. /// <param name="fieldName">The name of the Id field.</param>
  750. /// <returns>The member map (so method calls can be chained).</returns>
  751. public BsonMemberMap MapIdField(string fieldName)
  752. {
  753. if (fieldName == null)
  754. {
  755. throw new ArgumentNullException("fieldName");
  756. }
  757. if (_frozen) { ThrowFrozenException(); }
  758. var fieldMap = MapField(fieldName);
  759. SetIdMember(fieldMap);
  760. return fieldMap;
  761. }
  762. /// <summary>
  763. /// Creates a member map for the Id member and adds it to the class map.
  764. /// </summary>
  765. /// <param name="memberInfo">The member info for the Id member.</param>
  766. /// <returns>The member map (so method calls can be chained).</returns>
  767. public BsonMemberMap MapIdMember(MemberInfo memberInfo)
  768. {
  769. if (memberInfo == null)
  770. {
  771. throw new ArgumentNullException("memberInfo");
  772. }
  773. if (_frozen) { ThrowFrozenException(); }
  774. var memberMap = MapMember(memberInfo);
  775. SetIdMember(memberMap);
  776. return memberMap;
  777. }
  778. /// <summary>
  779. /// Creates a member map for the Id property and adds it to the class map.
  780. /// </summary>
  781. /// <param name="propertyName">The name of the Id property.</param>
  782. /// <returns>The member map (so method calls can be chained).</returns>
  783. public BsonMemberMap MapIdProperty(string propertyName)
  784. {
  785. if (propertyName == null)
  786. {
  787. throw new ArgumentNullException("propertyName");
  788. }
  789. if (_frozen) { ThrowFrozenException(); }
  790. var propertyMap = MapProperty(propertyName);
  791. SetIdMember(propertyMap);
  792. return propertyMap;
  793. }
  794. /// <summary>
  795. /// Creates a member map for a member and adds it to the class map.
  796. /// </summary>
  797. /// <param name="memberInfo">The member info.</param>
  798. /// <returns>The member map (so method calls can be chained).</returns>
  799. public BsonMemberMap MapMember(MemberInfo memberInfo)
  800. {
  801. if (memberInfo == null)
  802. {
  803. throw new ArgumentNullException("memberInfo");
  804. }
  805. if (!(memberInfo is FieldInfo) && !(memberInfo is PropertyInfo))
  806. {
  807. throw new ArgumentException("MemberInfo must be either a FieldInfo or a PropertyInfo.", "memberInfo");
  808. }
  809. EnsureMemberInfoIsForThisClass(memberInfo);
  810. if (_frozen) { ThrowFrozenException(); }
  811. var memberMap = _declaredMemberMaps.Find(m => m.MemberInfo == memberInfo);
  812. if (memberMap == null)
  813. {
  814. memberMap = new BsonMemberMap(this, memberInfo);
  815. _declaredMemberMaps.Add(memberMap);
  816. }
  817. return memberMap;
  818. }
  819. /// <summary>
  820. /// Creates a member map for a property and adds it to the class map.
  821. /// </summary>
  822. /// <param name="propertyName">The name of the property.</param>
  823. /// <returns>The member map (so method calls can be chained).</returns>
  824. public BsonMemberMap MapProperty(string propertyName)
  825. {
  826. if (propertyName == null)
  827. {
  828. throw new ArgumentNullException("propertyName");
  829. }
  830. if (_frozen) { ThrowFrozenException(); }
  831. var propertyInfo = _classType.GetTypeInfo().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  832. if (propertyInfo == null)
  833. {
  834. var message = string.Format("The class '{0}' does not have a property named '{1}'.", _classType.FullName, propertyName);
  835. throw new BsonSerializationException(message);
  836. }
  837. return MapMember(propertyInfo);
  838. }
  839. /// <summary>
  840. /// Resets the class map back to its initial state.
  841. /// </summary>
  842. public void Reset()
  843. {
  844. if (_frozen) { ThrowFrozenException(); }
  845. _creatorMaps.Clear();
  846. _creator = null;
  847. _declaredMemberMaps = new List<BsonMemberMap>();
  848. _discriminator = _classType.Name;
  849. _discriminatorIsRequired = false;
  850. _extraElementsMemberMap = null;
  851. _idMemberMap = null;
  852. _ignoreExtraElements = true; // TODO: should this really be false?
  853. _ignoreExtraElementsIsInherited = false;
  854. _isRootClass = false;
  855. _knownTypes.Clear();
  856. }
  857. /// <summary>
  858. /// Sets the creator for the object.
  859. /// </summary>
  860. /// <param name="creator">The creator.</param>
  861. /// <returns>The class map (so method calls can be chained).</returns>
  862. public BsonClassMap SetCreator(Func<object> creator)
  863. {
  864. _creator = creator;
  865. return this;
  866. }
  867. /// <summary>
  868. /// Sets the discriminator.
  869. /// </summary>
  870. /// <param name="discriminator">The discriminator.</param>
  871. public void SetDiscriminator(string discriminator)
  872. {
  873. if (discriminator == null)
  874. {
  875. throw new ArgumentNullException("discriminator");
  876. }
  877. if (_frozen) { ThrowFrozenException(); }
  878. _discriminator = discriminator;
  879. }
  880. /// <summary>
  881. /// Sets whether a discriminator is required when serializing this class.
  882. /// </summary>
  883. /// <param name="discriminatorIsRequired">Whether a discriminator is required.</param>
  884. public void SetDiscriminatorIsRequired(bool discriminatorIsRequired)
  885. {
  886. if (_frozen) { ThrowFrozenException(); }
  887. _discriminatorIsRequired = discriminatorIsRequired;
  888. }
  889. /// <summary>
  890. /// Sets the member map of the member used to hold extra elements.
  891. /// </summary>
  892. /// <param name="memberMap">The extra elements member map.</param>
  893. public void SetExtraElementsMember(BsonMemberMap memberMap)
  894. {
  895. if (memberMap == null)
  896. {
  897. throw new ArgumentNullException("memberMap");
  898. }
  899. EnsureMemberMapIsForThisClass(memberMap);
  900. if (_frozen) { ThrowFrozenException(); }
  901. if (memberMap.MemberType != typeof(BsonDocument) && !typeof(IDictionary<string, object>).GetTypeInfo().IsAssignableFrom(memberMap.MemberType))
  902. {
  903. var message = string.Format("Type of ExtraElements member must be BsonDocument or implement IDictionary<string, object>.");
  904. throw new InvalidOperationException(message);
  905. }
  906. _extraElementsMemberMap = memberMap;
  907. }
  908. /// <summary>
  909. /// Adds a known type to the class map.
  910. /// </summary>
  911. /// <param name="type">The known type.</param>
  912. public void AddKnownType(Type type)
  913. {
  914. if (!_classType.GetTypeInfo().IsAssignableFrom(type))
  915. {
  916. 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);
  917. throw new ArgumentNullException("type", message);
  918. }
  919. if (_frozen) { ThrowFrozenException(); }
  920. _knownTypes.Add(type);
  921. }
  922. /// <summary>
  923. /// Sets the Id member.
  924. /// </summary>
  925. /// <param name="memberMap">The Id member (null if none).</param>
  926. public void SetIdMember(BsonMemberMap memberMap)
  927. {
  928. if (memberMap != null)
  929. {
  930. EnsureMemberMapIsForThisClass(memberMap);
  931. }
  932. if (_frozen) { ThrowFrozenException(); }
  933. _idMemberMap = memberMap;
  934. }
  935. /// <summary>
  936. /// Sets whether extra elements should be ignored when deserializing.
  937. /// </summary>
  938. /// <param name="ignoreExtraElements">Whether extra elements should be ignored when deserializing.</param>
  939. public void SetIgnoreExtraElements(bool ignoreExtraElements)
  940. {
  941. if (_frozen) { ThrowFrozenException(); }
  942. _ignoreExtraElements = ignoreExtraElements;
  943. }
  944. /// <summary>
  945. /// Sets whether the IgnoreExtraElements value should be inherited by derived classes.
  946. /// </summary>
  947. /// <param name="ignoreExtraElementsIsInherited">Whether the IgnoreExtraElements value should be inherited by derived classes.</param>
  948. public void SetIgnoreExtraElementsIsInherited(bool ignoreExtraElementsIsInherited)
  949. {
  950. if (_frozen) { ThrowFrozenException(); }
  951. _ignoreExtraElementsIsInherited = ignoreExtraElementsIsInherited;
  952. }
  953. /// <summary>
  954. /// Sets whether this class is a root class.
  955. /// </summary>
  956. /// <param name="isRootClass">Whether this class is a root class.</param>
  957. public void SetIsRootClass(bool isRootClass)
  958. {
  959. if (_frozen) { ThrowFrozenException(); }
  960. _isRootClass = isRootClass;
  961. }
  962. /// <summary>
  963. /// Removes a creator map for a constructor from the class map.
  964. /// </summary>
  965. /// <param name="constructorInfo">The constructor info.</param>
  966. public void UnmapConstructor(ConstructorInfo constructorInfo)
  967. {
  968. if (constructorInfo == null)
  969. {
  970. throw new ArgumentNullException("constructorInfo");
  971. }
  972. EnsureMemberInfoIsForThisClass(constructorInfo);
  973. if (_frozen) { ThrowFrozenException(); }
  974. var creatorMap = _creatorMaps.FirstOrDefault(m => m.MemberInfo == constructorInfo);
  975. if (creatorMap != null)
  976. {
  977. _creatorMaps.Remove(creatorMap);
  978. }
  979. }
  980. /// <summary>
  981. /// Removes a creator map for a factory method from the class map.
  982. /// </summary>
  983. /// <param name="methodInfo">The method info.</param>
  984. public void UnmapFactoryMethod(MethodInfo methodInfo)
  985. {
  986. if (methodInfo == null)
  987. {
  988. throw new ArgumentNullException("methodInfo");
  989. }
  990. EnsureMemberInfoIsForThisClass(methodInfo);
  991. if (_frozen) { ThrowFrozenException(); }
  992. var creatorMap = _creatorMaps.FirstOrDefault(m => m.MemberInfo == methodInfo);
  993. if (creatorMap != null)
  994. {
  995. _creatorMaps.Remove(creatorMap);
  996. }
  997. }
  998. /// <summary>
  999. /// Removes the member map for a field from the class map.
  1000. /// </summary>
  1001. /// <param name="fieldName">The name of the field.</param>
  1002. public void UnmapField(string fieldName)
  1003. {
  1004. if (fieldName == null)
  1005. {
  1006. throw new ArgumentNullException("fieldName");
  1007. }
  1008. if (_frozen) { ThrowFrozenException(); }
  1009. var fieldInfo = _classType.GetTypeInfo().GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  1010. if (fieldInfo == null)
  1011. {
  1012. var message = string.Format("The class '{0}' does not have a field named '{1}'.", _classType.FullName, fieldName);
  1013. throw new BsonSerializationException(message);
  1014. }
  1015. UnmapMember(fieldInfo);
  1016. }
  1017. /// <summary>
  1018. /// Removes a member map from the class map.
  1019. /// </summary>
  1020. /// <param name="memberInfo">The member info.</param>
  1021. public void UnmapMember(MemberInfo memberInfo)
  1022. {
  1023. if (memberInfo == null)
  1024. {
  1025. throw new ArgumentNullException("memberInfo");
  1026. }
  1027. EnsureMemberInfoIsForThisClass(memberInfo);
  1028. if (_frozen) { ThrowFrozenException(); }
  1029. var memberMap = _declaredMemberMaps.Find(m => m.MemberInfo == memberInfo);
  1030. if (memberMap != null)
  1031. {
  1032. _declaredMemberMaps.Remove(memberMap);
  1033. if (_idMemberMap == memberMap)
  1034. {
  1035. _idMemberMap = null;
  1036. }
  1037. if (_extraElementsMemberMap == memberMap)
  1038. {
  1039. _extraElementsMemberMap = null;
  1040. }
  1041. }
  1042. }
  1043. /// <summary>
  1044. /// Removes the member map for a property from the class map.
  1045. /// </summary>
  1046. /// <param name="propertyName">The name of the property.</param>
  1047. public void UnmapProperty(string propertyName)
  1048. {
  1049. if (propertyName == null)
  1050. {
  1051. throw new ArgumentNullException("propertyName");
  1052. }
  1053. if (_frozen) { ThrowFrozenException(); }
  1054. var propertyInfo = _classType.GetTypeInfo().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  1055. if (propertyInfo == null)
  1056. {
  1057. var message = string.Format("The class '{0}' does not have a property named '{1}'.", _classType.FullName, propertyName);
  1058. throw new BsonSerializationException(message);
  1059. }
  1060. UnmapMember(propertyInfo);
  1061. }
  1062. // internal methods
  1063. /// <summary>
  1064. /// Gets the discriminator convention for the class.
  1065. /// </summary>
  1066. /// <returns>The discriminator convention for the class.</returns>
  1067. internal IDiscriminatorConvention GetDiscriminatorConvention()
  1068. {
  1069. // return a cached discriminator convention when possible
  1070. var discriminatorConvention = _discriminatorConvention;
  1071. if (discriminatorConvention == null)
  1072. {
  1073. // it's possible but harmless for multiple threads to do the initial lookup at the same time
  1074. discriminatorConvention = BsonSerializer.LookupDiscriminatorConvention(_classType);
  1075. _discriminatorConvention = discriminatorConvention;
  1076. }
  1077. return discriminatorConvention;
  1078. }
  1079. // private methods
  1080. private void AutoMapClass()
  1081. {
  1082. new ConventionRunner(_conventionPack).Apply(this);
  1083. foreach (var memberMap in _declaredMemberMaps)
  1084. {
  1085. TryFindShouldSerializeMethod(memberMap);
  1086. }
  1087. }
  1088. private void TryFindShouldSerializeMethod(BsonMemberMap memberMap)
  1089. {
  1090. // see if the class has a method called ShouldSerializeXyz where Xyz is the name of this member
  1091. var shouldSerializeMethod = GetShouldSerializeMethod(memberMap.MemberInfo);
  1092. if (shouldSerializeMethod != null)
  1093. {
  1094. memberMap.SetShouldSerializeMethod(shouldSerializeMethod);
  1095. }
  1096. }
  1097. private void EnsureMemberInfoIsForThisClass(MemberInfo memberInfo)
  1098. {
  1099. if (memberInfo.DeclaringType != _classType)
  1100. {
  1101. var message = string.Format(
  1102. "The memberInfo argument must be for class {0}, but was for class {1}.",
  1103. _classType.Name,
  1104. memberInfo.DeclaringType.Name);
  1105. throw new ArgumentOutOfRangeException("memberInfo", message);
  1106. }
  1107. }
  1108. private void EnsureMemberMapIsForThisClass(BsonMemberMap memberMap)
  1109. {
  1110. if (memberMap.ClassMap != this)
  1111. {
  1112. var message = string.Format(
  1113. "The memberMap argument must be for class {0}, but was for class {1}.",
  1114. _classType.Name,
  1115. memberMap.ClassMap.ClassType.Name);
  1116. throw new ArgumentOutOfRangeException("memberMap", message);
  1117. }
  1118. }
  1119. private Func<object> GetCreator()
  1120. {
  1121. if (_creator == null)
  1122. {
  1123. Expression body;
  1124. var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  1125. var classTypeInfo = _classType.GetTypeInfo();
  1126. var defaultConstructor = classTypeInfo.GetConstructors(bindingFlags)
  1127. .Where(c => c.GetParameters().Length == 0)
  1128. .SingleOrDefault();
  1129. if (defaultConstructor != null)
  1130. {
  1131. // lambdaExpression = () => (object) new TClass()
  1132. body = Expression.New(defaultConstructor);
  1133. }
  1134. else if (__getUninitializedObjectMethodInfo != null)
  1135. {
  1136. // lambdaExpression = () => FormatterServices.GetUninitializedObject(classType)
  1137. body = Expression.Call(__getUninitializedObjectMethodInfo, Expression.Constant(_classType));
  1138. }
  1139. else
  1140. {
  1141. var message = $"Type '{_classType.GetType().Name}' does not have a default constructor.";
  1142. throw new BsonSerializationException(message);
  1143. }
  1144. var lambdaExpression = Expression.Lambda<Func<object>>(body);
  1145. _creator = lambdaExpression.Compile();
  1146. }
  1147. return _creator;
  1148. }
  1149. private Func<object, bool> GetShouldSerializeMethod(MemberInfo memberInfo)
  1150. {
  1151. var shouldSerializeMethodName = "ShouldSerialize" + memberInfo.Name;
  1152. var shouldSerializeMethodInfo = _classType.GetTypeInfo().GetMethod(shouldSerializeMethodName, new Type[] { });
  1153. if (shouldSerializeMethodInfo != null &&
  1154. shouldSerializeMethodInfo.IsPublic &&
  1155. shouldSerializeMethodInfo.ReturnType == typeof(bool))
  1156. {
  1157. // lambdaExpression = (obj) => ((TClass) obj).ShouldSerializeXyz()
  1158. var objParameter = Expression.Parameter(typeof(object), "obj");
  1159. var lambdaExpression = Expression.Lambda<Func<object, bool>>(Expression.Call(Expression.Convert(objParameter, _classType), shouldSerializeMethodInfo), objParameter);
  1160. return lambdaExpression.Compile();
  1161. }
  1162. else
  1163. {
  1164. return null;
  1165. }
  1166. }
  1167. private bool IsAnonymousType(Type type)
  1168. {
  1169. // don't test for too many things in case implementation details change in the future
  1170. var typeInfo = type.GetTypeInfo();
  1171. return
  1172. typeInfo.GetCustomAttributes<CompilerGeneratedAttribute>(false).Any() &&
  1173. typeInfo.IsGenericType &&
  1174. type.Name.Contains("Anon"); // don't check for more than "Anon" so it works in mono also
  1175. }
  1176. private void ThrowFrozenException()
  1177. {
  1178. var message = string.Format("Class map for {0} has been frozen and no further changes are allowed.", _classType.FullName);
  1179. throw new InvalidOperationException(message);
  1180. }
  1181. private void ThrowNotFrozenException()
  1182. {
  1183. var message = string.Format("Class map for {0} has been not been frozen yet.", _classType.FullName);
  1184. throw new InvalidOperationException(message);
  1185. }
  1186. }
  1187. /// <summary>
  1188. /// Represents a mapping between a class and a BSON document.
  1189. /// </summary>
  1190. /// <typeparam name="TClass">The class.</typeparam>
  1191. public class BsonClassMap<TClass> : BsonClassMap
  1192. {
  1193. // constructors
  1194. /// <summary>
  1195. /// Initializes a new instance of the BsonClassMap class.
  1196. /// </summary>
  1197. public BsonClassMap()
  1198. : base(typeof(TClass))
  1199. {
  1200. }
  1201. /// <summary>
  1202. /// Initializes a new instance of the BsonClassMap class.
  1203. /// </summary>
  1204. /// <param name="classMapInitializer">The class map initializer.</param>
  1205. public BsonClassMap(Action<BsonClassMap<TClass>> classMapInitializer)
  1206. : base(typeof(TClass))
  1207. {
  1208. classMapInitializer(this);
  1209. }
  1210. // public methods
  1211. /// <summary>
  1212. /// Creates an instance.
  1213. /// </summary>
  1214. /// <returns>An instance.</returns>
  1215. public new TClass CreateInstance()
  1216. {
  1217. return (TClass)base.CreateInstance();
  1218. }
  1219. /// <summary>
  1220. /// Gets a member map.
  1221. /// </summary>
  1222. /// <typeparam name="TMember">The member type.</typeparam>
  1223. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1224. /// <returns>The member map.</returns>
  1225. public BsonMemberMap GetMemberMap<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1226. {
  1227. var memberName = GetMemberNameFromLambda(memberLambda);
  1228. return GetMemberMap(memberName);
  1229. }
  1230. /// <summary>
  1231. /// Creates a creator map and adds it to the class map.
  1232. /// </summary>
  1233. /// <param name="creatorLambda">Lambda expression specifying the creator code and parameters to use.</param>
  1234. /// <returns>The member map.</returns>
  1235. public BsonCreatorMap MapCreator(Expression<Func<TClass, TClass>> creatorLambda)
  1236. {
  1237. if (creatorLambda == null)
  1238. {
  1239. throw new ArgumentNullException("creatorLambda");
  1240. }
  1241. IEnumerable<MemberInfo> arguments;
  1242. var @delegate = new CreatorMapDelegateCompiler().CompileCreatorDelegate(creatorLambda, out arguments);
  1243. var creatorMap = MapCreator(@delegate);
  1244. creatorMap.SetArguments(arguments);
  1245. return creatorMap;
  1246. }
  1247. /// <summary>
  1248. /// Creates a member map for the extra elements field and adds it to the class map.
  1249. /// </summary>
  1250. /// <typeparam name="TMember">The member type.</typeparam>
  1251. /// <param name="fieldLambda">A lambda expression specifying the extra elements field.</param>
  1252. /// <returns>The member map.</returns>
  1253. public BsonMemberMap MapExtraElementsField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1254. {
  1255. var fieldMap = MapField(fieldLambda);
  1256. SetExtraElementsMember(fieldMap);
  1257. return fieldMap;
  1258. }
  1259. /// <summary>
  1260. /// Creates a member map for the extra elements member and adds it to the class map.
  1261. /// </summary>
  1262. /// <typeparam name="TMember">The member type.</typeparam>
  1263. /// <param name="memberLambda">A lambda expression specifying the extra elements member.</param>
  1264. /// <returns>The member map.</returns>
  1265. public BsonMemberMap MapExtraElementsMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1266. {
  1267. var memberMap = MapMember(memberLambda);
  1268. SetExtraElementsMember(memberMap);
  1269. return memberMap;
  1270. }
  1271. /// <summary>
  1272. /// Creates a member map for the extra elements property and adds it to the class map.
  1273. /// </summary>
  1274. /// <typeparam name="TMember">The member type.</typeparam>
  1275. /// <param name="propertyLambda">A lambda expression specifying the extra elements property.</param>
  1276. /// <returns>The member map.</returns>
  1277. public BsonMemberMap MapExtraElementsProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1278. {
  1279. var propertyMap = MapProperty(propertyLambda);
  1280. SetExtraElementsMember(propertyMap);
  1281. return propertyMap;
  1282. }
  1283. /// <summary>
  1284. /// Creates a member map for a field and adds it to the class map.
  1285. /// </summary>
  1286. /// <typeparam name="TMember">The member type.</typeparam>
  1287. /// <param name="fieldLambda">A lambda expression specifying the field.</param>
  1288. /// <returns>The member map.</returns>
  1289. public BsonMemberMap MapField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1290. {
  1291. return MapMember(fieldLambda);
  1292. }
  1293. /// <summary>
  1294. /// Creates a member map for the Id field and adds it to the class map.
  1295. /// </summary>
  1296. /// <typeparam name="TMember">The member type.</typeparam>
  1297. /// <param name="fieldLambda">A lambda expression specifying the Id field.</param>
  1298. /// <returns>The member map.</returns>
  1299. public BsonMemberMap MapIdField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1300. {
  1301. var fieldMap = MapField(fieldLambda);
  1302. SetIdMember(fieldMap);
  1303. return fieldMap;
  1304. }
  1305. /// <summary>
  1306. /// Creates a member map for the Id member and adds it to the class map.
  1307. /// </summary>
  1308. /// <typeparam name="TMember">The member type.</typeparam>
  1309. /// <param name="memberLambda">A lambda expression specifying the Id member.</param>
  1310. /// <returns>The member map.</returns>
  1311. public BsonMemberMap MapIdMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1312. {
  1313. var memberMap = MapMember(memberLambda);
  1314. SetIdMember(memberMap);
  1315. return memberMap;
  1316. }
  1317. /// <summary>
  1318. /// Creates a member map for the Id property and adds it to the class map.
  1319. /// </summary>
  1320. /// <typeparam name="TMember">The member type.</typeparam>
  1321. /// <param name="propertyLambda">A lambda expression specifying the Id property.</param>
  1322. /// <returns>The member map.</returns>
  1323. public BsonMemberMap MapIdProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1324. {
  1325. var propertyMap = MapProperty(propertyLambda);
  1326. SetIdMember(propertyMap);
  1327. return propertyMap;
  1328. }
  1329. /// <summary>
  1330. /// Creates a member map and adds it to the class map.
  1331. /// </summary>
  1332. /// <typeparam name="TMember">The member type.</typeparam>
  1333. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1334. /// <returns>The member map.</returns>
  1335. public BsonMemberMap MapMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1336. {
  1337. var memberInfo = GetMemberInfoFromLambda(memberLambda);
  1338. return MapMember(memberInfo);
  1339. }
  1340. /// <summary>
  1341. /// Creates a member map for the Id property and adds it to the class map.
  1342. /// </summary>
  1343. /// <typeparam name="TMember">The member type.</typeparam>
  1344. /// <param name="propertyLambda">A lambda expression specifying the Id property.</param>
  1345. /// <returns>The member map.</returns>
  1346. public BsonMemberMap MapProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1347. {
  1348. return MapMember(propertyLambda);
  1349. }
  1350. /// <summary>
  1351. /// Removes the member map for a field from the class map.
  1352. /// </summary>
  1353. /// <typeparam name="TMember">The member type.</typeparam>
  1354. /// <param name="fieldLambda">A lambda expression specifying the field.</param>
  1355. public void UnmapField<TMember>(Expression<Func<TClass, TMember>> fieldLambda)
  1356. {
  1357. UnmapMember(fieldLambda);
  1358. }
  1359. /// <summary>
  1360. /// Removes a member map from the class map.
  1361. /// </summary>
  1362. /// <typeparam name="TMember">The member type.</typeparam>
  1363. /// <param name="memberLambda">A lambda expression specifying the member.</param>
  1364. public void UnmapMember<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1365. {
  1366. var memberInfo = GetMemberInfoFromLambda(memberLambda);
  1367. UnmapMember(memberInfo);
  1368. }
  1369. /// <summary>
  1370. /// Removes a member map for a property from the class map.
  1371. /// </summary>
  1372. /// <typeparam name="TMember">The member type.</typeparam>
  1373. /// <param name="propertyLambda">A lambda expression specifying the property.</param>
  1374. public void UnmapProperty<TMember>(Expression<Func<TClass, TMember>> propertyLambda)
  1375. {
  1376. UnmapMember(propertyLambda);
  1377. }
  1378. // private static methods
  1379. private static MethodInfo[] GetPropertyAccessors(PropertyInfo propertyInfo)
  1380. {
  1381. return propertyInfo.GetAccessors(true);
  1382. }
  1383. private static MemberInfo GetMemberInfoFromLambda<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1384. {
  1385. var body = memberLambda.Body;
  1386. MemberExpression memberExpression;
  1387. switch (body.NodeType)
  1388. {
  1389. case ExpressionType.MemberAccess:
  1390. memberExpression = (MemberExpression)body;
  1391. break;
  1392. case ExpressionType.Convert:
  1393. var convertExpression = (UnaryExpression)body;
  1394. memberExpression = (MemberExpression)convertExpression.Operand;
  1395. break;
  1396. default:
  1397. throw new BsonSerializationException("Invalid lambda expression");
  1398. }
  1399. var memberInfo = memberExpression.Member;
  1400. if (memberInfo is PropertyInfo)
  1401. {
  1402. if (memberInfo.DeclaringType.GetTypeInfo().IsInterface)
  1403. {
  1404. memberInfo = FindPropertyImplementation((PropertyInfo)memberInfo, typeof(TClass));
  1405. }
  1406. }
  1407. else if (!(memberInfo is FieldInfo))
  1408. {
  1409. throw new BsonSerializationException("Invalid lambda expression");
  1410. }
  1411. return memberInfo;
  1412. }
  1413. private static string GetMemberNameFromLambda<TMember>(Expression<Func<TClass, TMember>> memberLambda)
  1414. {
  1415. return GetMemberInfoFromLambda(memberLambda).Name;
  1416. }
  1417. private static PropertyInfo FindPropertyImplementation(PropertyInfo interfacePropertyInfo, Type actualType)
  1418. {
  1419. var interfaceType = interfacePropertyInfo.DeclaringType;
  1420. #if NETSTANDARD1_5 || NETSTANDARD1_6 || NETCOREAPP
  1421. var actualTypeInfo = actualType.GetTypeInfo();
  1422. var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
  1423. var actualTypePropertyInfos = actualTypeInfo.GetMembers(bindingFlags).OfType<PropertyInfo>();
  1424. var explicitlyImplementedPropertyName = $"{interfacePropertyInfo.DeclaringType.FullName}.{interfacePropertyInfo.Name}".Replace("+", ".");
  1425. var explicitlyImplementedPropertyInfo = actualTypePropertyInfos
  1426. .Where(p => p.Name == explicitlyImplementedPropertyName)
  1427. .SingleOrDefault();
  1428. if (explicitlyImplementedPropertyInfo != null)
  1429. {
  1430. return explicitlyImplementedPropertyInfo;
  1431. }
  1432. var implicitlyImplementedPropertyInfo = actualTypePropertyInfos
  1433. .Where(p => p.Name == interfacePropertyInfo.Name && p.PropertyType == interfacePropertyInfo.PropertyType)
  1434. .SingleOrDefault();
  1435. if (implicitlyImplementedPropertyInfo != null)
  1436. {
  1437. return implicitlyImplementedPropertyInfo;
  1438. }
  1439. throw new BsonSerializationException($"Unable to find property info for property: '{interfacePropertyInfo.Name}'.");
  1440. #else
  1441. // An interface map must be used because because there is no
  1442. // other officially documented way to derive the explicitly
  1443. // implemented property name.
  1444. var interfaceMap = actualType.GetInterfaceMap(interfaceType);
  1445. var interfacePropertyAccessors = GetPropertyAccessors(interfacePropertyInfo);
  1446. var actualPropertyAccessors = interfacePropertyAccessors.Select(interfacePropertyAccessor =>
  1447. {
  1448. var index = Array.IndexOf<MethodInfo>(interfaceMap.InterfaceMethods, interfacePropertyAccessor);
  1449. return interfaceMap.TargetMethods[index];
  1450. });
  1451. // Binding must be done by accessor methods because interface
  1452. // maps only map accessor methods and do not map properties.
  1453. return actualType.GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
  1454. .Single(propertyInfo =>
  1455. {
  1456. // we are looking for a property that implements all the required accessors
  1457. var propertyAccessors = GetPropertyAccessors(propertyInfo);
  1458. return actualPropertyAccessors.All(x => propertyAccessors.Contains(x));
  1459. });
  1460. #endif
  1461. }
  1462. }
  1463. }