BsonMemberMap.cs 22 KB

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