FieldDefinition.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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;
  17. using System.Linq.Expressions;
  18. using MongoDB.Bson.Serialization;
  19. using MongoDB.Bson.Serialization.Serializers;
  20. using MongoDB.Driver.Core.Misc;
  21. using MongoDB.Driver.Linq;
  22. using MongoDB.Driver.Linq.Expressions;
  23. using MongoDB.Driver.Linq.Processors;
  24. using MongoDB.Driver.Linq.Translators;
  25. namespace MongoDB.Driver
  26. {
  27. /// <summary>
  28. /// A rendered field.
  29. /// </summary>
  30. public sealed class RenderedFieldDefinition
  31. {
  32. private readonly string _fieldName;
  33. private readonly IBsonSerializer _fieldSerializer;
  34. /// <summary>
  35. /// Initializes a new instance of the <see cref="RenderedFieldDefinition{TField}" /> class.
  36. /// </summary>
  37. /// <param name="fieldName">The field name.</param>
  38. /// <param name="fieldSerializer">The field serializer.</param>
  39. public RenderedFieldDefinition(string fieldName, IBsonSerializer fieldSerializer = null)
  40. {
  41. _fieldName = Ensure.IsNotNull(fieldName, nameof(fieldName));
  42. _fieldSerializer = fieldSerializer;
  43. }
  44. /// <summary>
  45. /// Gets the field name.
  46. /// </summary>
  47. public string FieldName
  48. {
  49. get { return _fieldName; }
  50. }
  51. /// <summary>
  52. /// Gets the field serializer.
  53. /// </summary>
  54. public IBsonSerializer FieldSerializer
  55. {
  56. get { return _fieldSerializer; }
  57. }
  58. }
  59. /// <summary>
  60. /// A rendered field.
  61. /// </summary>
  62. /// <typeparam name="TField">The type of the field.</typeparam>
  63. public sealed class RenderedFieldDefinition<TField>
  64. {
  65. private readonly string _fieldName;
  66. private readonly IBsonSerializer<TField> _fieldSerializer;
  67. private readonly IBsonSerializer _underlyingSerializer;
  68. private readonly IBsonSerializer<TField> _valueSerializer;
  69. /// <summary>
  70. /// Initializes a new instance of the <see cref="RenderedFieldDefinition{TField}" /> class.
  71. /// </summary>
  72. /// <param name="fieldName">The field name.</param>
  73. /// <param name="fieldSerializer">The field serializer.</param>
  74. [Obsolete("Use the constructor that takes 4 arguments instead.")]
  75. public RenderedFieldDefinition(string fieldName, IBsonSerializer<TField> fieldSerializer)
  76. : this(fieldName, fieldSerializer, fieldSerializer, fieldSerializer)
  77. {
  78. }
  79. /// <summary>
  80. /// Initializes a new instance of the <see cref="RenderedFieldDefinition{TField}" /> class.
  81. /// </summary>
  82. /// <param name="fieldName">The field name.</param>
  83. /// <param name="fieldSerializer">The field serializer.</param>
  84. /// <param name="valueSerializer">The value serializer.</param>
  85. /// <param name="underlyingSerializer">The underlying serializer.</param>
  86. public RenderedFieldDefinition(string fieldName, IBsonSerializer<TField> fieldSerializer, IBsonSerializer<TField> valueSerializer, IBsonSerializer underlyingSerializer)
  87. {
  88. _fieldName = Ensure.IsNotNull(fieldName, nameof(fieldName));
  89. _fieldSerializer = fieldSerializer;
  90. _valueSerializer = Ensure.IsNotNull(valueSerializer, nameof(valueSerializer));
  91. _underlyingSerializer = underlyingSerializer;
  92. }
  93. /// <summary>
  94. /// Gets the field name.
  95. /// </summary>
  96. public string FieldName
  97. {
  98. get { return _fieldName; }
  99. }
  100. /// <summary>
  101. /// Gets the field serializer.
  102. /// </summary>
  103. public IBsonSerializer<TField> FieldSerializer
  104. {
  105. get { return _fieldSerializer; }
  106. }
  107. /// <summary>
  108. /// Gets the underlying serializer.
  109. /// </summary>
  110. public IBsonSerializer UnderlyingSerializer
  111. {
  112. get { return _underlyingSerializer; }
  113. }
  114. /// <summary>
  115. /// Gets the value serializer.
  116. /// </summary>
  117. public IBsonSerializer<TField> ValueSerializer
  118. {
  119. get { return _valueSerializer; }
  120. }
  121. }
  122. /// <summary>
  123. /// Base class for field names.
  124. /// </summary>
  125. /// <typeparam name="TDocument">The type of the document.</typeparam>
  126. public abstract class FieldDefinition<TDocument>
  127. {
  128. /// <summary>
  129. /// Renders the field to a <see cref="String"/>.
  130. /// </summary>
  131. /// <param name="documentSerializer">The document serializer.</param>
  132. /// <param name="serializerRegistry">The serializer registry.</param>
  133. /// <returns>A <see cref="String"/>.</returns>
  134. public abstract RenderedFieldDefinition Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry);
  135. /// <summary>
  136. /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="FieldDefinition{TDocument}"/>.
  137. /// </summary>
  138. /// <param name="fieldName">Name of the field.</param>
  139. /// <returns>
  140. /// The result of the conversion.
  141. /// </returns>
  142. public static implicit operator FieldDefinition<TDocument>(string fieldName)
  143. {
  144. if (fieldName == null)
  145. {
  146. return null;
  147. }
  148. return new StringFieldDefinition<TDocument>(fieldName);
  149. }
  150. }
  151. /// <summary>
  152. /// Base class for field names.
  153. /// </summary>
  154. /// <typeparam name="TDocument">The type of the document.</typeparam>
  155. /// <typeparam name="TField">The type of the field.</typeparam>
  156. public abstract class FieldDefinition<TDocument, TField>
  157. {
  158. /// <summary>
  159. /// Renders the field to a <see cref="String"/>.
  160. /// </summary>
  161. /// <param name="documentSerializer">The document serializer.</param>
  162. /// <param name="serializerRegistry">The serializer registry.</param>
  163. /// <returns>A <see cref="String"/>.</returns>
  164. public abstract RenderedFieldDefinition<TField> Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry);
  165. /// <summary>
  166. /// Performs an implicit conversion from <see cref="System.String" /> to <see cref="FieldDefinition{TDocument, TField}" />.
  167. /// </summary>
  168. /// <param name="fieldName">Name of the field.</param>
  169. /// <returns>
  170. /// The result of the conversion.
  171. /// </returns>
  172. public static implicit operator FieldDefinition<TDocument, TField>(string fieldName)
  173. {
  174. if (fieldName == null)
  175. {
  176. return null;
  177. }
  178. return new StringFieldDefinition<TDocument, TField>(fieldName, null);
  179. }
  180. /// <summary>
  181. /// Performs an implicit conversion from <see cref="FieldDefinition{TDocument, TField}"/> to <see cref="FieldDefinition{TDocument}"/>.
  182. /// </summary>
  183. /// <param name="field">The field.</param>
  184. /// <returns>
  185. /// The result of the conversion.
  186. /// </returns>
  187. public static implicit operator FieldDefinition<TDocument>(FieldDefinition<TDocument, TField> field)
  188. {
  189. return new UntypedFieldDefinitionAdapter<TDocument, TField>(field);
  190. }
  191. }
  192. /// <summary>
  193. /// An <see cref="Expression" /> based field.
  194. /// </summary>
  195. /// <typeparam name="TDocument">The type of the document.</typeparam>
  196. public sealed class ExpressionFieldDefinition<TDocument> : FieldDefinition<TDocument>
  197. {
  198. private readonly LambdaExpression _expression;
  199. /// <summary>
  200. /// Initializes a new instance of the <see cref="ExpressionFieldDefinition{TDocument}" /> class.
  201. /// </summary>
  202. /// <param name="expression">The expression.</param>
  203. public ExpressionFieldDefinition(LambdaExpression expression)
  204. {
  205. _expression = Ensure.IsNotNull(expression, nameof(expression));
  206. if (expression.Parameters.Count != 1)
  207. {
  208. throw new ArgumentException("Only a single parameter lambda expression is allowed.", "expression");
  209. }
  210. if (expression.Parameters[0].Type != typeof(TDocument))
  211. {
  212. var message = string.Format("The lambda expression parameter must be of type {0}.", typeof(TDocument));
  213. throw new ArgumentException(message, "expression");
  214. }
  215. }
  216. /// <summary>
  217. /// Gets the expression.
  218. /// </summary>
  219. public LambdaExpression Expression
  220. {
  221. get { return _expression; }
  222. }
  223. /// <inheritdoc />
  224. public override RenderedFieldDefinition Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  225. {
  226. var bindingContext = new PipelineBindingContext(serializerRegistry);
  227. var lambda = ExpressionHelper.GetLambda(PartialEvaluator.Evaluate(_expression));
  228. var parameterExpression = new DocumentExpression(documentSerializer);
  229. bindingContext.AddExpressionMapping(lambda.Parameters[0], parameterExpression);
  230. var bound = bindingContext.Bind(lambda.Body);
  231. bound = FieldExpressionFlattener.FlattenFields(bound);
  232. IFieldExpression field;
  233. if (!ExpressionHelper.TryGetExpression(bound, out field))
  234. {
  235. var message = string.Format("Unable to determine the serialization information for {0}.", _expression);
  236. throw new InvalidOperationException(message);
  237. }
  238. return new RenderedFieldDefinition(field.FieldName, field.Serializer);
  239. }
  240. }
  241. /// <summary>
  242. /// An <see cref="Expression" /> based field.
  243. /// </summary>
  244. /// <typeparam name="TDocument">The type of the document.</typeparam>
  245. /// <typeparam name="TField">The type of the field.</typeparam>
  246. public sealed class ExpressionFieldDefinition<TDocument, TField> : FieldDefinition<TDocument, TField>
  247. {
  248. private readonly Expression<Func<TDocument, TField>> _expression;
  249. /// <summary>
  250. /// Initializes a new instance of the <see cref="ExpressionFieldDefinition{TDocument, TField}" /> class.
  251. /// </summary>
  252. /// <param name="expression">The expression.</param>
  253. public ExpressionFieldDefinition(Expression<Func<TDocument, TField>> expression)
  254. {
  255. _expression = Ensure.IsNotNull(expression, nameof(expression));
  256. }
  257. /// <summary>
  258. /// Gets the expression.
  259. /// </summary>
  260. public Expression<Func<TDocument, TField>> Expression
  261. {
  262. get { return _expression; }
  263. }
  264. /// <inheritdoc />
  265. public override RenderedFieldDefinition<TField> Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  266. {
  267. var lambda = (LambdaExpression)PartialEvaluator.Evaluate(_expression);
  268. var bindingContext = new PipelineBindingContext(serializerRegistry);
  269. var parameterExpression = new DocumentExpression(documentSerializer);
  270. bindingContext.AddExpressionMapping(lambda.Parameters[0], parameterExpression);
  271. var bound = bindingContext.Bind(lambda.Body);
  272. bound = FieldExpressionFlattener.FlattenFields(bound);
  273. IFieldExpression field;
  274. if (!Linq.ExpressionHelper.TryGetExpression(bound, out field))
  275. {
  276. var message = string.Format("Unable to determine the serialization information for {0}.", _expression);
  277. throw new InvalidOperationException(message);
  278. }
  279. var underlyingSerializer = field.Serializer;
  280. var fieldSerializer = underlyingSerializer as IBsonSerializer<TField>;
  281. var valueSerializer = (IBsonSerializer<TField>)FieldValueSerializerHelper.GetSerializerForValueType(underlyingSerializer, serializerRegistry, typeof(TField));
  282. return new RenderedFieldDefinition<TField>(field.FieldName, fieldSerializer, valueSerializer, underlyingSerializer);
  283. }
  284. }
  285. /// <summary>
  286. /// A <see cref="String" /> based field name.
  287. /// </summary>
  288. /// <typeparam name="TDocument">The type of the document.</typeparam>
  289. public sealed class StringFieldDefinition<TDocument> : FieldDefinition<TDocument>
  290. {
  291. private readonly string _fieldName;
  292. /// <summary>
  293. /// Initializes a new instance of the <see cref="StringFieldDefinition{TDocument}" /> class.
  294. /// </summary>
  295. /// <param name="fieldName">Name of the field.</param>
  296. public StringFieldDefinition(string fieldName)
  297. {
  298. _fieldName = Ensure.IsNotNull(fieldName, nameof(fieldName));
  299. }
  300. /// <inheritdoc />
  301. public override RenderedFieldDefinition Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  302. {
  303. string resolvedName;
  304. IBsonSerializer resolvedSerializer;
  305. StringFieldDefinitionHelper.Resolve<TDocument>(_fieldName, documentSerializer, out resolvedName, out resolvedSerializer);
  306. return new RenderedFieldDefinition(resolvedName, resolvedSerializer);
  307. }
  308. }
  309. /// <summary>
  310. /// A <see cref="String" /> based field name.
  311. /// </summary>
  312. /// <typeparam name="TDocument">The type of the document.</typeparam>
  313. /// <typeparam name="TField">The type of the field.</typeparam>
  314. public sealed class StringFieldDefinition<TDocument, TField> : FieldDefinition<TDocument, TField>
  315. {
  316. private readonly string _fieldName;
  317. private readonly IBsonSerializer<TField> _fieldSerializer;
  318. /// <summary>
  319. /// Initializes a new instance of the <see cref="StringFieldDefinition{TDocument, TField}" /> class.
  320. /// </summary>
  321. /// <param name="fieldName">Name of the field.</param>
  322. /// <param name="fieldSerializer">The field serializer.</param>
  323. public StringFieldDefinition(string fieldName, IBsonSerializer<TField> fieldSerializer = null)
  324. {
  325. _fieldName = Ensure.IsNotNull(fieldName, nameof(fieldName));
  326. _fieldSerializer = fieldSerializer;
  327. }
  328. /// <inheritdoc />
  329. public override RenderedFieldDefinition<TField> Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  330. {
  331. string resolvedName;
  332. IBsonSerializer underlyingSerializer;
  333. StringFieldDefinitionHelper.Resolve<TDocument>(_fieldName, documentSerializer, out resolvedName, out underlyingSerializer);
  334. var fieldSerializer = underlyingSerializer as IBsonSerializer<TField>;
  335. IBsonSerializer<TField> valueSerializer;
  336. if (_fieldSerializer != null)
  337. {
  338. valueSerializer = _fieldSerializer;
  339. }
  340. else if (underlyingSerializer != null)
  341. {
  342. valueSerializer = (IBsonSerializer<TField>)FieldValueSerializerHelper.GetSerializerForValueType(underlyingSerializer, serializerRegistry, typeof(TField));
  343. }
  344. else
  345. {
  346. valueSerializer = serializerRegistry.GetSerializer<TField>();
  347. }
  348. return new RenderedFieldDefinition<TField>(resolvedName, fieldSerializer, valueSerializer, underlyingSerializer);
  349. }
  350. }
  351. internal static class StringFieldDefinitionHelper
  352. {
  353. public static void Resolve<TDocument>(string fieldName, IBsonSerializer<TDocument> serializer, out string resolvedFieldName, out IBsonSerializer resolvedFieldSerializer)
  354. {
  355. resolvedFieldName = fieldName;
  356. resolvedFieldSerializer = null;
  357. var documentSerializer = serializer as IBsonDocumentSerializer;
  358. if (documentSerializer == null)
  359. {
  360. return;
  361. }
  362. // shortcut BsonDocumentSerializer since it is so common
  363. if (serializer.GetType() == typeof(BsonDocumentSerializer))
  364. {
  365. return;
  366. }
  367. BsonSerializationInfo serializationInfo;
  368. // first, lets try the quick and easy one, which will be a majority of cases
  369. if (documentSerializer.TryGetMemberSerializationInfo(fieldName, out serializationInfo))
  370. {
  371. resolvedFieldName = serializationInfo.ElementName;
  372. resolvedFieldSerializer = serializationInfo.Serializer;
  373. return;
  374. }
  375. // now lets go and do the more difficult variant
  376. var nameParts = fieldName.Split('.');
  377. if (nameParts.Length <= 1)
  378. {
  379. // if we only have 1, then it's no different than what we did above
  380. // when we found nothing.
  381. return;
  382. }
  383. IBsonArraySerializer arraySerializer;
  384. resolvedFieldSerializer = documentSerializer;
  385. for (int i = 0; i < nameParts.Length; i++)
  386. {
  387. if (nameParts[i] == "$" || nameParts[i].All(char.IsDigit))
  388. {
  389. arraySerializer = resolvedFieldSerializer as IBsonArraySerializer;
  390. if (arraySerializer != null && arraySerializer.TryGetItemSerializationInfo(out serializationInfo))
  391. {
  392. resolvedFieldSerializer = serializationInfo.Serializer;
  393. continue;
  394. }
  395. resolvedFieldSerializer = null;
  396. break;
  397. }
  398. documentSerializer = resolvedFieldSerializer as IBsonDocumentSerializer;
  399. if (documentSerializer == null || !documentSerializer.TryGetMemberSerializationInfo(nameParts[i], out serializationInfo))
  400. {
  401. // need to check if this is an any element array match
  402. arraySerializer = resolvedFieldSerializer as IBsonArraySerializer;
  403. if (arraySerializer != null && arraySerializer.TryGetItemSerializationInfo(out serializationInfo))
  404. {
  405. documentSerializer = serializationInfo.Serializer as IBsonDocumentSerializer;
  406. if (documentSerializer == null || !documentSerializer.TryGetMemberSerializationInfo(nameParts[i], out serializationInfo))
  407. {
  408. resolvedFieldSerializer = null;
  409. break;
  410. }
  411. }
  412. else
  413. {
  414. resolvedFieldSerializer = null;
  415. break;
  416. }
  417. }
  418. nameParts[i] = serializationInfo.ElementName;
  419. resolvedFieldSerializer = serializationInfo.Serializer;
  420. }
  421. resolvedFieldName = string.Join(".", nameParts);
  422. }
  423. }
  424. internal class UntypedFieldDefinitionAdapter<TDocument, TField> : FieldDefinition<TDocument>
  425. {
  426. private readonly FieldDefinition<TDocument, TField> _adaptee;
  427. public UntypedFieldDefinitionAdapter(FieldDefinition<TDocument, TField> adaptee)
  428. {
  429. _adaptee = Ensure.IsNotNull(adaptee, nameof(adaptee));
  430. }
  431. public override RenderedFieldDefinition Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
  432. {
  433. var rendered = _adaptee.Render(documentSerializer, serializerRegistry);
  434. return new RenderedFieldDefinition(rendered.FieldName, rendered.UnderlyingSerializer);
  435. }
  436. }
  437. }