BsonClassMap.cs 60 KB

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