Search code examples
c#mongodblinqmongodb-.net-driver

MongoDB C# - LINQ Contains against a string array throws ArgumentException


I'm new to MongoDB so this might be a naive question, yet I have not found any relevant/up to date information by googling around: I am trying to use the MongoDB C# driver (version 2.2.4) to compose a LINQ-based query, one piece at a time, from a received filter POCO object, like this:

IQueryable<BsonDocument> parts = collection.AsQueryable();
if (filter.Name != null)
    parts = parts.Where(d => d["Name"].Equals(filter.Name));
// ... etc; I'll then call ToList() to get results ...

Now, one of my filter properties is a string array, meaning that I should match any document whose field Vendor (a string property in the MongoDB document) is equal to any of the strings in the array (like MongoDB native $in: https://docs.mongodb.com/manual/reference/operator/query/in/).

To this end, I tried with Contains (the special case for a 1-sized array is just an optimization):

if (filter.Vendors != null && filter.Vendors.Length > 0)
{
    parts = filter.Vendors.Length == 1
        ? parts.Where(d => d["Vendor"].Equals(filter.Vendors[0]))
        : parts.Where(d => filter.Vendors.Contains(d["Vendor"].AsString));
}

This compiles, but throws an ArgumentException: "Expression of type 'MongoDB.Bson.BsonValue' cannot be used for parameter of type 'System.String' of method 'Boolean Contains[String](System.Collections.Generic.IEnumerable`1[System.String], System.String)'".

Looking at http://mongodb.github.io/mongo-csharp-driver/2.2/reference/driver/crud/linq/, there is nothing about Contains or $in; yet, from https://jira.mongodb.org/browse/CSHARP-462 it seems that the driver should now be capable of handling that method.

BTW, the same happens if I slightly change the code to:

   parts.Where(d => filter.Vendors.Any(s=> d["Vendor"].Equals(s)));

which does not involve Contains at all. The exception message complains about not being able to use BsonValue for string, yet that BsonValue is right a string. Could anyone suggest a solution?


Solution

  • The exception messages dance around the idea of fully embracing BsonValue to let mongo handle the types instead of trying to cast to string. I got it to work having Vendors as type List<BsonValue>.

    class Filter
    { 
            public List<BsonValue> Vendors { get; set; }
    }
    
    ...
    
    var list = parts.Where(d => filter.Vendors.Contains(d["Vendor"]));
    foreach (var document in list)
    {
        Console.WriteLine(document["Name"]);
    }
    

    Another alternative is to map your documents to a C# class instead of using BsonDocument as the collection type.

    class MyDocument
    {
        public ObjectId Id { get; set; }
        public string Name { get; set; }
        public string Vendor { get; set; }
    }
    ...
    var collection = db.GetCollection <MyDocument> ("parts");