2 / 2
Mar 12

Hi!

While migrating from LinqProvider.V2 to LinqProvider.V3, I encountered the following issue. I have a custom value type UserId, which includes explicit conversion operators for Guid. The following query worked fine in LinqProvider.V2, but after switching to LinqProvider.V3, it throws an exception:

var user = await collection.Find(Builders<User>.Filter.In(x => (Guid)x.ExternalId, new[] { guid })) .FirstOrDefaultAsync();

Exception:

MongoDB.Driver.Linq.ExpressionNotSupportedException : Expression not supported: Convert(x.ExternalId, Guid).

Are there any workarounds for this issue in LinqProvider.V3?
Any suggestions would be appreciated!


Full Reproduction Code:

using System; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; using MongoDB.Driver.Linq; using NUnit.Framework; namespace MongoCases { [TestFixture] public class MongoCases_Linq3_ExplicitConversion : Mongo2GoFixture { [Test] public async Task Test() { BsonSerializer.RegisterSerializer(new UserIdBsonSerializer()); var mongoClientSettings = MongoClientSettings.FromConnectionString(ConnectionString); mongoClientSettings.LinqProvider = LinqProvider.V3; var mongoClient = new MongoClient(mongoClientSettings); var db = mongoClient.GetDatabase(Guid.NewGuid().ToString()[..8]); var collection = db.GetCollection<User>("users"); var guid = Guid.NewGuid(); await collection.InsertOneAsync(new User { ExternalId = (UserId)guid }); var user = await collection.Find(Builders<User>.Filter.In(x => (Guid)x.ExternalId, new[] { guid })) .FirstOrDefaultAsync(); // MongoDB.Driver.Linq.ExpressionNotSupportedException : Expression not supported: Convert(x.ExternalId, Guid). Assert.That(user, Is.Not.Null); } private class User { [BsonId] public ObjectId Id { get; set; } [BsonElement("externalId")] public UserId ExternalId { get; set; } } private readonly struct UserId { private readonly Guid _id; public UserId(Guid id) { _id = id; } public static explicit operator Guid(UserId source) => source._id; public static explicit operator UserId(Guid id) => new UserId(id); } private class UserIdBsonSerializer : StructSerializerBase<UserId> { private readonly GuidSerializer _guidSerializer = new GuidSerializer(); public override UserId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => new UserId(_guidSerializer.Deserialize(context, args)); public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, UserId value) => _guidSerializer.Serialize(context, args, (Guid)value); } } }

I know that I can slightly rewrite the query like this:

var user = await collection.Find(Builders<User>.Filter.In(x => x.ExternalId, new[] { guid }.Select(x => (UserId)guid))).FirstOrDefaultAsync();

However, there are many similar queries across different projects, so I’d prefer not to rewrite all of them.

Is there a way to globally allow explicit casting for certain types in LinqProvider.V3?