Details
-
Bug
-
Resolution: Works as Designed
-
Unknown
-
None
-
None
-
None
Description
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.