BsonMemberMap.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  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.Reflection;
  17. using MongoDB.Bson.Serialization.Serializers;
  18. namespace MongoDB.Bson.Serialization
  19. {
  20. /// <summary>
  21. /// Represents the mapping between a field or property and a BSON element.
  22. /// </summary>
  23. public class BsonMemberMap
  24. {
  25. // private fields
  26. private readonly BsonClassMap _classMap;
  27. private readonly MemberInfo _memberInfo;
  28. private readonly Type _memberType;
  29. private readonly bool _memberTypeIsBsonValue;
  30. private string _elementName;
  31. private bool _frozen; // once a class map has been frozen no further changes are allowed
  32. private int _order;
  33. private Func<object, object> _getter;
  34. private Action<object, object> _setter;
  35. private volatile IBsonSerializer _serializer;
  36. private IIdGenerator _idGenerator;
  37. private bool _isRequired;
  38. private Func<object, bool> _shouldSerializeMethod;
  39. private bool _ignoreIfDefault;
  40. private bool _ignoreIfNull;
  41. private object _defaultValue;
  42. private Func<object> _defaultValueCreator;
  43. private bool _defaultValueSpecified;
  44. // constructors
  45. /// <summary>
  46. /// Initializes a new instance of the BsonMemberMap class.
  47. /// </summary>
  48. /// <param name="classMap">The class map this member map belongs to.</param>
  49. /// <param name="memberInfo">The member info.</param>
  50. public BsonMemberMap(BsonClassMap classMap, MemberInfo memberInfo)
  51. {
  52. _classMap = classMap;
  53. _memberInfo = memberInfo;
  54. _memberType = BsonClassMap.GetMemberInfoType(memberInfo);
  55. _memberTypeIsBsonValue = typeof(BsonValue).GetTypeInfo().IsAssignableFrom(_memberType);
  56. Reset();
  57. }
  58. // public properties
  59. /// <summary>
  60. /// Gets the class map that this member map belongs to.
  61. /// </summary>
  62. public BsonClassMap ClassMap
  63. {
  64. get { return _classMap; }
  65. }
  66. /// <summary>
  67. /// Gets the name of the member.
  68. /// </summary>
  69. public string MemberName
  70. {
  71. get { return _memberInfo.Name; }
  72. }
  73. /// <summary>
  74. /// Gets the type of the member.
  75. /// </summary>
  76. public Type MemberType
  77. {
  78. get { return _memberType; }
  79. }
  80. /// <summary>
  81. /// Gets whether the member type is a BsonValue.
  82. /// </summary>
  83. public bool MemberTypeIsBsonValue
  84. {
  85. get { return _memberTypeIsBsonValue; }
  86. }
  87. /// <summary>
  88. /// Gets the name of the element.
  89. /// </summary>
  90. public string ElementName
  91. {
  92. get { return _elementName; }
  93. }
  94. /// <summary>
  95. /// Gets the serialization order.
  96. /// </summary>
  97. public int Order
  98. {
  99. get { return _order; }
  100. }
  101. /// <summary>
  102. /// Gets the member info.
  103. /// </summary>
  104. public MemberInfo MemberInfo
  105. {
  106. get { return _memberInfo; }
  107. }
  108. /// <summary>
  109. /// Gets the getter function.
  110. /// </summary>
  111. public Func<object, object> Getter
  112. {
  113. get
  114. {
  115. if (_getter == null)
  116. {
  117. _getter = GetGetter();
  118. }
  119. return _getter;
  120. }
  121. }
  122. /// <summary>
  123. /// Gets the setter function.
  124. /// </summary>
  125. public Action<object, object> Setter
  126. {
  127. get
  128. {
  129. if (_setter == null)
  130. {
  131. if (_memberInfo is FieldInfo)
  132. {
  133. _setter = GetFieldSetter();
  134. }
  135. else
  136. {
  137. _setter = GetPropertySetter();
  138. }
  139. }
  140. return _setter;
  141. }
  142. }
  143. /// <summary>
  144. /// Gets the Id generator.
  145. /// </summary>
  146. public IIdGenerator IdGenerator
  147. {
  148. get { return _idGenerator; }
  149. }
  150. /// <summary>
  151. /// Gets whether a default value was specified.
  152. /// </summary>
  153. public bool IsDefaultValueSpecified
  154. {
  155. get { return _defaultValueSpecified; }
  156. }
  157. /// <summary>
  158. /// Gets whether an element is required for this member when deserialized.
  159. /// </summary>
  160. public bool IsRequired
  161. {
  162. get { return _isRequired; }
  163. }
  164. /// <summary>
  165. /// Gets the method that will be called to determine whether the member should be serialized.
  166. /// </summary>
  167. public Func<object, bool> ShouldSerializeMethod
  168. {
  169. get { return _shouldSerializeMethod; }
  170. }
  171. /// <summary>
  172. /// Gets whether default values should be ignored when serialized.
  173. /// </summary>
  174. public bool IgnoreIfDefault
  175. {
  176. get { return _ignoreIfDefault; }
  177. }
  178. /// <summary>
  179. /// Gets whether null values should be ignored when serialized.
  180. /// </summary>
  181. public bool IgnoreIfNull
  182. {
  183. get { return _ignoreIfNull; }
  184. }
  185. /// <summary>
  186. /// Gets the default value.
  187. /// </summary>
  188. public object DefaultValue
  189. {
  190. get { return _defaultValueCreator != null ? _defaultValueCreator() : _defaultValue; }
  191. }
  192. /// <summary>
  193. /// Gets whether the member is readonly.
  194. /// </summary>
  195. /// <remarks>
  196. /// Readonly indicates that the member is written to the database, but not read from the database.
  197. /// </remarks>
  198. public bool IsReadOnly
  199. {
  200. get
  201. {
  202. if (_memberInfo is FieldInfo)
  203. {
  204. var field = (FieldInfo)_memberInfo;
  205. return field.IsInitOnly || field.IsLiteral;
  206. }
  207. else if (_memberInfo is PropertyInfo)
  208. {
  209. var property = (PropertyInfo)_memberInfo;
  210. return !property.CanWrite;
  211. }
  212. else
  213. {
  214. throw new NotSupportedException(
  215. string.Format("Only fields and properties are supported by BsonMemberMap. The member {0} of class {1} is a {2}.",
  216. _memberInfo.Name,
  217. _memberInfo.DeclaringType.Name,
  218. _memberInfo is FieldInfo ? "field" : "property"));
  219. }
  220. }
  221. }
  222. // public methods
  223. /// <summary>
  224. /// Applies the default value to the member of an object.
  225. /// </summary>
  226. /// <param name="obj">The object.</param>
  227. public void ApplyDefaultValue(object obj)
  228. {
  229. if (_defaultValueSpecified)
  230. {
  231. this.Setter(obj, DefaultValue);
  232. }
  233. }
  234. /// <summary>
  235. /// Freezes this instance.
  236. /// </summary>
  237. public void Freeze()
  238. {
  239. _frozen = true;
  240. }
  241. /// <summary>
  242. /// Gets the serializer.
  243. /// </summary>
  244. /// <returns>The serializer.</returns>
  245. public IBsonSerializer GetSerializer()
  246. {
  247. if (_serializer == null)
  248. {
  249. // return special serializer for BsonValue members that handles the _csharpnull representation
  250. if (_memberTypeIsBsonValue)
  251. {
  252. var wrappedSerializer = BsonSerializer.LookupSerializer(_memberType);
  253. var isBsonArraySerializer = wrappedSerializer is IBsonArraySerializer;
  254. var isBsonDocumentSerializer = wrappedSerializer is IBsonDocumentSerializer;
  255. Type csharpNullSerializerDefinition;
  256. if (isBsonArraySerializer && isBsonDocumentSerializer)
  257. {
  258. csharpNullSerializerDefinition = typeof(BsonValueCSharpNullArrayAndDocumentSerializer<>);
  259. }
  260. else if (isBsonArraySerializer)
  261. {
  262. csharpNullSerializerDefinition = typeof(BsonValueCSharpNullArraySerializer<>);
  263. }
  264. else if (isBsonDocumentSerializer)
  265. {
  266. csharpNullSerializerDefinition = typeof(BsonValueCSharpNullDocumentSerializer<>);
  267. }
  268. else
  269. {
  270. csharpNullSerializerDefinition = typeof(BsonValueCSharpNullSerializer<>);
  271. }
  272. var csharpNullSerializerType = csharpNullSerializerDefinition.MakeGenericType(_memberType);
  273. var csharpNullSerializer = (IBsonSerializer)Activator.CreateInstance(csharpNullSerializerType, wrappedSerializer);
  274. _serializer = csharpNullSerializer;
  275. }
  276. else
  277. {
  278. _serializer = BsonSerializer.LookupSerializer(_memberType);
  279. }
  280. }
  281. return _serializer;
  282. }
  283. /// <summary>
  284. /// Resets the member map back to its initial state.
  285. /// </summary>
  286. /// <returns>The member map.</returns>
  287. public BsonMemberMap Reset()
  288. {
  289. if (_frozen) { ThrowFrozenException(); }
  290. _defaultValue = GetDefaultValue(_memberType);
  291. _defaultValueCreator = null;
  292. _defaultValueSpecified = false;
  293. _elementName = _memberInfo.Name;
  294. _idGenerator = null;
  295. _ignoreIfDefault = false;
  296. _ignoreIfNull = false;
  297. _isRequired = false;
  298. _order = int.MaxValue;
  299. _serializer = null;
  300. _shouldSerializeMethod = null;
  301. return this;
  302. }
  303. /// <summary>
  304. /// Sets the default value creator.
  305. /// </summary>
  306. /// <param name="defaultValueCreator">The default value creator (note: the supplied delegate must be thread safe).</param>
  307. /// <returns>The member map.</returns>
  308. public BsonMemberMap SetDefaultValue(Func<object> defaultValueCreator)
  309. {
  310. if (defaultValueCreator == null)
  311. {
  312. throw new ArgumentNullException("defaultValueCreator");
  313. }
  314. if (_frozen) { ThrowFrozenException(); }
  315. _defaultValue = defaultValueCreator(); // need an instance to compare against
  316. _defaultValueCreator = defaultValueCreator;
  317. _defaultValueSpecified = true;
  318. return this;
  319. }
  320. /// <summary>
  321. /// Sets the default value.
  322. /// </summary>
  323. /// <param name="defaultValue">The default value.</param>
  324. /// <returns>The member map.</returns>
  325. public BsonMemberMap SetDefaultValue(object defaultValue)
  326. {
  327. if (_frozen) { ThrowFrozenException(); }
  328. _defaultValue = defaultValue;
  329. _defaultValueCreator = null;
  330. _defaultValueSpecified = true;
  331. return this;
  332. }
  333. /// <summary>
  334. /// Sets the name of the element.
  335. /// </summary>
  336. /// <param name="elementName">The name of the element.</param>
  337. /// <returns>The member map.</returns>
  338. public BsonMemberMap SetElementName(string elementName)
  339. {
  340. if (elementName == null)
  341. {
  342. throw new ArgumentNullException("elementName");
  343. }
  344. if (elementName.IndexOf('\0') != -1)
  345. {
  346. throw new ArgumentException("Element names cannot contain nulls.", "elementName");
  347. }
  348. if (_frozen) { ThrowFrozenException(); }
  349. _elementName = elementName;
  350. return this;
  351. }
  352. /// <summary>
  353. /// Sets the Id generator.
  354. /// </summary>
  355. /// <param name="idGenerator">The Id generator.</param>
  356. /// <returns>The member map.</returns>
  357. public BsonMemberMap SetIdGenerator(IIdGenerator idGenerator)
  358. {
  359. if (_frozen) { ThrowFrozenException(); }
  360. _idGenerator = idGenerator;
  361. return this;
  362. }
  363. /// <summary>
  364. /// Sets whether default values should be ignored when serialized.
  365. /// </summary>
  366. /// <param name="ignoreIfDefault">Whether default values should be ignored when serialized.</param>
  367. /// <returns>The member map.</returns>
  368. public BsonMemberMap SetIgnoreIfDefault(bool ignoreIfDefault)
  369. {
  370. if (_frozen) { ThrowFrozenException(); }
  371. if (ignoreIfDefault && _ignoreIfNull)
  372. {
  373. throw new InvalidOperationException("IgnoreIfDefault and IgnoreIfNull are mutually exclusive. Choose one or the other.");
  374. }
  375. _ignoreIfDefault = ignoreIfDefault;
  376. return this;
  377. }
  378. /// <summary>
  379. /// Sets whether null values should be ignored when serialized.
  380. /// </summary>
  381. /// <param name="ignoreIfNull">Wether null values should be ignored when serialized.</param>
  382. /// <returns>The member map.</returns>
  383. public BsonMemberMap SetIgnoreIfNull(bool ignoreIfNull)
  384. {
  385. if (_frozen) { ThrowFrozenException(); }
  386. if (ignoreIfNull && _ignoreIfDefault)
  387. {
  388. throw new InvalidOperationException("IgnoreIfDefault and IgnoreIfNull are mutually exclusive. Choose one or the other.");
  389. }
  390. _ignoreIfNull = ignoreIfNull;
  391. return this;
  392. }
  393. /// <summary>
  394. /// Sets whether an element is required for this member when deserialized
  395. /// </summary>
  396. /// <param name="isRequired">Whether an element is required for this member when deserialized</param>
  397. /// <returns>The member map.</returns>
  398. public BsonMemberMap SetIsRequired(bool isRequired)
  399. {
  400. if (_frozen) { ThrowFrozenException(); }
  401. _isRequired = isRequired;
  402. return this;
  403. }
  404. /// <summary>
  405. /// Sets the serialization order.
  406. /// </summary>
  407. /// <param name="order">The serialization order.</param>
  408. /// <returns>The member map.</returns>
  409. public BsonMemberMap SetOrder(int order)
  410. {
  411. if (_frozen) { ThrowFrozenException(); }
  412. _order = order;
  413. return this;
  414. }
  415. /// <summary>
  416. /// Sets the serializer.
  417. /// </summary>
  418. /// <param name="serializer">The serializer.</param>
  419. /// <returns>
  420. /// The member map.
  421. /// </returns>
  422. /// <exception cref="System.ArgumentNullException">serializer</exception>
  423. /// <exception cref="System.ArgumentException">serializer</exception>
  424. public BsonMemberMap SetSerializer(IBsonSerializer serializer)
  425. {
  426. if (serializer == null)
  427. {
  428. throw new ArgumentNullException("serializer");
  429. }
  430. if (serializer.ValueType != _memberType)
  431. {
  432. var message = string.Format("Value type of serializer is {0} and does not match member type {1}.", serializer.ValueType.FullName, _memberType.FullName);
  433. throw new ArgumentException(message, "serializer");
  434. }
  435. if (_frozen) { ThrowFrozenException(); }
  436. _serializer = serializer;
  437. return this;
  438. }
  439. /// <summary>
  440. /// Sets the method that will be called to determine whether the member should be serialized.
  441. /// </summary>
  442. /// <param name="shouldSerializeMethod">The method.</param>
  443. /// <returns>The member map.</returns>
  444. public BsonMemberMap SetShouldSerializeMethod(Func<object, bool> shouldSerializeMethod)
  445. {
  446. if (_frozen) { ThrowFrozenException(); }
  447. _shouldSerializeMethod = shouldSerializeMethod;
  448. return this;
  449. }
  450. /// <summary>
  451. /// Determines whether a value should be serialized
  452. /// </summary>
  453. /// <param name="obj">The object.</param>
  454. /// <param name="value">The value.</param>
  455. /// <returns>True if the value should be serialized.</returns>
  456. public bool ShouldSerialize(object obj, object value)
  457. {
  458. if (_ignoreIfNull)
  459. {
  460. if (value == null)
  461. {
  462. return false; // don't serialize null
  463. }
  464. }
  465. if (_ignoreIfDefault)
  466. {
  467. if (object.Equals(_defaultValue, value))
  468. {
  469. return false; // don't serialize default value
  470. }
  471. }
  472. if (_shouldSerializeMethod != null && !_shouldSerializeMethod(obj))
  473. {
  474. // the _shouldSerializeMethod determined that the member shouldn't be serialized
  475. return false;
  476. }
  477. return true;
  478. }
  479. // private methods
  480. private static object GetDefaultValue(Type type)
  481. {
  482. var typeInfo = type.GetTypeInfo();
  483. if (typeInfo.IsEnum)
  484. {
  485. return Enum.ToObject(type, 0);
  486. }
  487. switch (Type.GetTypeCode(type))
  488. {
  489. case TypeCode.Empty:
  490. #if NET452
  491. case TypeCode.DBNull:
  492. #endif
  493. case TypeCode.String:
  494. break;
  495. case TypeCode.Object:
  496. if (typeInfo.IsValueType)
  497. {
  498. return Activator.CreateInstance(type);
  499. }
  500. break;
  501. case TypeCode.Boolean: return false;
  502. case TypeCode.Char: return '\0';
  503. case TypeCode.SByte: return (sbyte)0;
  504. case TypeCode.Byte: return (byte)0;
  505. case TypeCode.Int16: return (short)0;
  506. case TypeCode.UInt16: return (ushort)0;
  507. case TypeCode.Int32: return 0;
  508. case TypeCode.UInt32: return 0U;
  509. case TypeCode.Int64: return 0L;
  510. case TypeCode.UInt64: return 0UL;
  511. case TypeCode.Single: return 0F;
  512. case TypeCode.Double: return 0D;
  513. case TypeCode.Decimal: return 0M;
  514. case TypeCode.DateTime: return DateTime.MinValue;
  515. }
  516. return null;
  517. }
  518. private Action<object, object> GetFieldSetter()
  519. {
  520. FieldInfo fieldInfo = (FieldInfo) _memberInfo;
  521. if (IsReadOnly)
  522. {
  523. string message =
  524. $"The field '{fieldInfo.FieldType.FullName} {fieldInfo.Name}' of class '{fieldInfo.DeclaringType.FullName}' is readonly. To avoid this exception, call IsReadOnly to ensure that setting a value is allowed.";
  525. throw new BsonSerializationException(message);
  526. }
  527. return (obj, value) => { fieldInfo.SetValue(obj, value); };
  528. }
  529. private Func<object, object> GetGetter()
  530. {
  531. PropertyInfo propertyInfo = _memberInfo as PropertyInfo;
  532. if (propertyInfo != null)
  533. {
  534. MethodInfo getMethodInfo = propertyInfo.GetMethod;
  535. if (getMethodInfo == null)
  536. {
  537. string message =
  538. $"The property '{propertyInfo.PropertyType.FullName} {propertyInfo.Name}' of class '{propertyInfo.DeclaringType.FullName}' has no 'get' accessor.";
  539. throw new BsonSerializationException(message);
  540. }
  541. return obj => { return propertyInfo.GetValue(obj); };
  542. }
  543. FieldInfo fieldInfo = _memberInfo as FieldInfo;
  544. return obj => { return fieldInfo.GetValue(obj); };
  545. }
  546. private Action<object, object> GetPropertySetter()
  547. {
  548. PropertyInfo propertyInfo = (PropertyInfo) _memberInfo;
  549. return (obj, value) => { propertyInfo.SetValue(obj, value); };
  550. }
  551. private void ThrowFrozenException()
  552. {
  553. var message = string.Format("Member map for {0}.{1} has been frozen and no further changes are allowed.", _classMap.ClassType.FullName, _memberInfo.Name);
  554. throw new InvalidOperationException(message);
  555. }
  556. }
  557. }