BsonMemberMap.cs 23 KB

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