[CSHARP-921] Any subquery fails for Nullable<ObjectId> comparison Created: 05/Mar/14  Updated: 23/Jun/15  Resolved: 05/Mar/14

Status: Closed
Project: C# Driver
Component/s: None
Affects Version/s: 1.8.3
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Michael Kennedy Assignee: Unassigned
Resolution: Won't Fix Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: Text File Product.cs     Text File Supplier.cs    
Backwards Compatibility: Fully Compatible

 Description   

Take these queries and try to run them. You will get the following exception:

ObjectId?[] supplierIds =
(from s in mongo.Suppliers
where s.Country == supplierCountry
select new Nullable<ObjectId>(s.Id)).ToArray();

var supplierProducts =
(from p in mongo.Products
where supplierIds.Any(id => id == p.SupplierID)
orderby p.ProductName
select p);

Unhandled Exception: System.NotSupportedException: Unable to determine the serialization information for the expression: Nullable<ObjectId>[]:

{ 000000000000000000000016, 000000000000000000000019, 000000000000000000000002, 000000000000000000000003 }

.
at MongoDB.Driver.Linq.Utils.BsonSerializationInfoFinder.GetSerializationInfo
(Expression node, Dictionary`2 serializationInfoCache)
at MongoDB.Driver.Linq.PredicateTranslator.BuildAnyQuery(MethodCallExpression
methodCallExpression)
at MongoDB.Driver.Linq.PredicateTranslator.BuildMethodCallQuery(MethodCallExp
ression methodCallExpression)
at MongoDB.Driver.Linq.PredicateTranslator.BuildQuery(Expression expression)
at MongoDB.Driver.Linq.SelectQuery.BuildQuery()
at MongoDB.Driver.Linq.SelectQuery.Execute()
at MongoDB.Driver.Linq.MongoQueryProvider.Execute(Expression expression)
at MongoDB.Driver.Linq.MongoQueryable`1.GetEnumerator()

An alternate attempt at that query with ObjectId[] rather than ObjectId[] gives basically the same failure:

ObjectId[] supplierIds =
(from s in mongo.Suppliers
where s.Country == supplierCountry
select s.Id).ToArray();

var supplierProducts =
(from p in mongo.Products
where supplierIds.Any(id => id == p.SupplierID)
orderby p.ProductName
select p);

However, if I use Contains rather than Any, it works! But it is more painful because I need to convert to an nullable ObjectId array when the source is not actually nullable (required so supplierIds.Contains() compiles):

ObjectId?[] supplierIds =
(from s in mongo.Suppliers
where s.Country == supplierCountry
select new Nullable<ObjectId>(s.Id)).ToArray();

var supplierProducts =
(from p in mongo.Products
where supplierIds.Contains(p.SupplierID)
orderby p.ProductName
select p);

I've attached the two related classes involved in the query.



 Comments   
Comment by Michael Kennedy [ 05/Mar/14 ]

Make sense. It's slightly more painful to write the contains because of the type mismatch but that's somewhat a weakness of C# not being able to answer does a nullable value type live in a non-nullable typed array.

Thanks for the feedback.

Comment by Craig Wilson [ 05/Mar/14 ]

Interesting use case. I'm inclined to mark this as won't fix unless I'm missing something.

What gets translated when using Contains on a local "array" is an $in clause is generated. {x: { $in : [1,2,3]}}. The problem with supporting Any in this fashion is that there is only 1 version of Any that would actually be legitimate (and it happens to be the one you are using). It takes the form of localArray.Any(item => item == value). If any other type of comparison is used inside the Any predicate, it no longer has a valid translation to MongoDB query syntax (or at least an efficient one).

As such, using Contains on the local "array" is the supported form for this type of query. Thoughts?

Comment by Michael Kennedy [ 05/Mar/14 ]

I should probably have entitled this

IQueryable<T>.Any subquery fails for ObjectId[] comparison.

I originally wanted to use ObjectId[] but had to move to ObjectId?[] to use Contains (which is effectively the same but works).

Generated at Wed Feb 07 21:38:12 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.