-
Type: Bug
-
Resolution: Works as Designed
-
Priority: Unknown
-
None
-
Affects Version/s: None
-
Component/s: LINQ3
-
Labels:None
In my codebase I have a custom type defined together with the custom serializer for that type. After switching to linq v3 several types of queries are exploding with the following error:
Unhandled exception. System.ArgumentException: Invalid toType: System.Guid. (Parameter ‘toType’) at MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions.AstExpression.Convert(AstExpression input, Type toType, AstExpression onError, AstExpression onNull) …
Is there a way to configure the driver so the query rewrite won’t be necessary? Below full repro (driver 2.17.1):
using System; using System.Linq; using System.Linq.Expressions; using System.Security.Authentication; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; using MongoDB.Driver.Linq; BsonSerializer.RegisterSerializer(typeof(Guid), new GuidSerializer(BsonType.String)); BsonSerializer.RegisterSerializer(typeof(InvoiceId), new MyGuidSerializer()); BsonTypeMapper.RegisterCustomTypeMapper(typeof(InvoiceId), new MyGuidBsonTypeMapper()); var settings = MongoClientSettings.FromUrl(new MongoUrl("mongodb://localhost:27017/test")); settings.SslSettings = new SslSettings {EnabledSslProtocols = SslProtocols.Tls12}; settings.LinqProvider = LinqProvider.V3; var mongoClient = new MongoClient(settings); var mongoDatabase = mongoClient.GetDatabase("test"); mongoDatabase.DropCollection("test"); var collection = mongoDatabase.GetCollection<Document>("test"); var guid = Guid.NewGuid(); var invoiceId = new InvoiceId(guid); var guidNullable = (Guid?) guid; var invoiceIdNullable = (InvoiceId?) invoiceId; var document = new Document { InvoiceId = invoiceId, InvoiceIdNullable = invoiceId, Guid = guid, GuidNullable = guid }; collection.InsertOne(document); Expression<Func<Document, bool>>[] f = { c => c.Guid == guid, c => c.GuidNullable == guid, c => c.Guid == invoiceId, c => c.GuidNullable == invoiceId, c => c.InvoiceId == invoiceId, c => c.InvoiceIdNullable == invoiceId, c => c.Guid == guidNullable, c => c.GuidNullable == guidNullable, c => c.Guid == invoiceIdNullable, c => c.GuidNullable == invoiceIdNullable, c => c.InvoiceId == invoiceIdNullable, c => c.InvoiceIdNullable == invoiceIdNullable, c => c.InvoiceId == guidNullable, // explodes in V3 c => c.InvoiceIdNullable == guidNullable, // explodes in V3 c => c.InvoiceId == guid, // explodes in V3 c => c.InvoiceIdNullable == guid, // explodes in V3 }; foreach (var expression in f) { Console.Out.WriteLine(expression.ToString()); var results = collection.AsQueryable().Where(expression).ToCursor().ToList(); var result = results.FirstOrDefault() ?? throw new Exception("Not found!"); if (result.InvoiceId != invoiceId) { throw new Exception("Mismatch!"); } Console.Out.WriteLine("All good"); } public class Document { public ObjectId Id { get; set; } public InvoiceId InvoiceId { get; set; } public InvoiceId? InvoiceIdNullable { get; set; } public Guid Guid { get; set; } public Guid? GuidNullable { get; set; } } public readonly record struct InvoiceId(Guid Value) { public static implicit operator Guid(InvoiceId s) => s.Value; } public class MyGuidSerializer : SerializerBase<InvoiceId> { public override InvoiceId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { if (context.Reader.CurrentBsonType == BsonType.Null) { context.Reader.ReadNull(); return default; } if (Guid.TryParse(context.Reader.ReadString(), out var guid)) { return new InvoiceId(guid); } return new InvoiceId(default); } public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, InvoiceId value) { context.Writer.WriteString(value.Value.ToString()); } } public class MyGuidBsonTypeMapper : ICustomBsonTypeMapper { public bool TryMapToBsonValue(object value, out BsonValue bsonValue) { bsonValue = (BsonString)((InvoiceId)value).Value.ToString(); return true; } }
Originally from this MongoDB Community Forums post filed by Marek Olszewski.