|
15 | 15 |
|
16 | 16 | using System;
|
17 | 17 | using System.Collections;
|
| 18 | +using System.Collections.Generic; |
18 | 19 | using System.Linq;
|
19 | 20 | using System.Linq.Expressions;
|
20 | 21 | using System.Text.RegularExpressions;
|
21 | 22 |
|
22 | 23 | using MongoDB.Bson;
|
23 | 24 | using MongoDB.Bson.IO;
|
24 | 25 | using MongoDB.Bson.Serialization;
|
| 26 | +using MongoDB.Bson.Serialization.Options; |
25 | 27 | using MongoDB.Driver.Builders;
|
26 | 28 | using MongoDB.Driver.Linq.Utils;
|
27 | 29 |
|
@@ -375,8 +377,94 @@ private IMongoQuery BuildContainsAnyQuery(MethodCallExpression methodCallExpress
|
375 | 377 | return null;
|
376 | 378 | }
|
377 | 379 |
|
| 380 | + private IMongoQuery BuildContainsKeyQuery(MethodCallExpression methodCallExpression) |
| 381 | + { |
| 382 | + var dictionaryType = methodCallExpression.Object.Type; |
| 383 | + var implementedInterfaces = new List<Type>(dictionaryType.GetInterfaces()); |
| 384 | + if (dictionaryType.IsInterface) |
| 385 | + { |
| 386 | + implementedInterfaces.Add(dictionaryType); |
| 387 | + } |
| 388 | + |
| 389 | + Type dictionaryGenericInterface = null; |
| 390 | + Type dictionaryInterface = null; |
| 391 | + foreach (var implementedInterface in implementedInterfaces) |
| 392 | + { |
| 393 | + if (implementedInterface.IsGenericType) |
| 394 | + { |
| 395 | + if (implementedInterface.GetGenericTypeDefinition() == typeof(IDictionary<,>)) |
| 396 | + { |
| 397 | + dictionaryGenericInterface = implementedInterface; |
| 398 | + } |
| 399 | + } |
| 400 | + else if (implementedInterface == typeof(IDictionary)) |
| 401 | + { |
| 402 | + dictionaryInterface = implementedInterface; |
| 403 | + } |
| 404 | + } |
| 405 | + |
| 406 | + Type keyNominalType; |
| 407 | + if (dictionaryGenericInterface != null) |
| 408 | + { |
| 409 | + keyNominalType = dictionaryGenericInterface.GetGenericArguments()[0]; // TKey |
| 410 | + } |
| 411 | + else if (dictionaryInterface != null) |
| 412 | + { |
| 413 | + keyNominalType = typeof(object); |
| 414 | + } |
| 415 | + else |
| 416 | + { |
| 417 | + return null; |
| 418 | + } |
| 419 | + |
| 420 | + var arguments = methodCallExpression.Arguments.ToArray(); |
| 421 | + if (arguments.Length != 1) |
| 422 | + { |
| 423 | + return null; |
| 424 | + } |
| 425 | + |
| 426 | + var constantExpression = arguments[0] as ConstantExpression; |
| 427 | + if (constantExpression == null) |
| 428 | + { |
| 429 | + return null; |
| 430 | + } |
| 431 | + var key = constantExpression.Value; |
| 432 | + |
| 433 | + var serializationInfo = _serializationInfoHelper.GetSerializationInfo(methodCallExpression.Object); |
| 434 | + var dictionarySerializationOptions = (DictionarySerializationOptions)serializationInfo.SerializationOptions ?? DictionarySerializationOptions.Defaults; |
| 435 | + |
| 436 | + var keyActualType = (key != null) ? key.GetType() : keyNominalType; |
| 437 | + var keySerializer = BsonSerializer.LookupSerializer(keyActualType); |
| 438 | + var keySerializationInfo = new BsonSerializationInfo( |
| 439 | + null, // elementName |
| 440 | + keySerializer, |
| 441 | + keyNominalType, |
| 442 | + dictionarySerializationOptions.KeyValuePairSerializationOptions.KeySerializationOptions); |
| 443 | + var serializedKey = _serializationInfoHelper.SerializeValue(keySerializationInfo, key); |
| 444 | + |
| 445 | + switch (dictionarySerializationOptions.Representation) |
| 446 | + { |
| 447 | + case DictionaryRepresentation.ArrayOfDocuments: |
| 448 | + return Query.EQ(serializationInfo.ElementName + ".k", serializedKey); |
| 449 | + case DictionaryRepresentation.Document: |
| 450 | + return Query.Exists(serializationInfo.ElementName + "." + serializedKey.AsString, true); |
| 451 | + default: |
| 452 | + var message = string.Format( |
| 453 | + "{0} in a LINQ query is only supported for DictionaryRepresentation ArrayOfDocuments or Document, not {1}.", |
| 454 | + methodCallExpression.Method.Name, // could be Contains (for IDictionary) or ContainsKey (for IDictionary<TKey, TValue>) |
| 455 | + dictionarySerializationOptions.Representation); |
| 456 | + throw new NotSupportedException(message); |
| 457 | + } |
| 458 | + } |
| 459 | + |
378 | 460 | private IMongoQuery BuildContainsQuery(MethodCallExpression methodCallExpression)
|
379 | 461 | {
|
| 462 | + // handle IDictionary Contains the same way as IDictionary<TKey, TValue> ContainsKey |
| 463 | + if (typeof(IDictionary).IsAssignableFrom(methodCallExpression.Object.Type)) |
| 464 | + { |
| 465 | + return BuildContainsKeyQuery(methodCallExpression); |
| 466 | + } |
| 467 | + |
380 | 468 | if (methodCallExpression.Method.DeclaringType == typeof(string))
|
381 | 469 | {
|
382 | 470 | return BuildStringQuery(methodCallExpression);
|
@@ -611,6 +699,7 @@ private IMongoQuery BuildMethodCallQuery(MethodCallExpression methodCallExpressi
|
611 | 699 | case "Contains": return BuildContainsQuery(methodCallExpression);
|
612 | 700 | case "ContainsAll": return BuildContainsAllQuery(methodCallExpression);
|
613 | 701 | case "ContainsAny": return BuildContainsAnyQuery(methodCallExpression);
|
| 702 | + case "ContainsKey": return BuildContainsKeyQuery(methodCallExpression); |
614 | 703 | case "EndsWith": return BuildStringQuery(methodCallExpression);
|
615 | 704 | case "Equals": return BuildEqualsQuery(methodCallExpression);
|
616 | 705 | case "In": return BuildInQuery(methodCallExpression);
|
|
0 commit comments