1 / 1
Apr 2024

Hello,

PullFilter on Guid array works fine with LinqProvider.V2, but V3 throws exception:

System.InvalidCastException HResult=0x80004002 Message=Unable to cast object of type 'MongoDB.Bson.Serialization.Serializers.GuidSerializer' to type 'MongoDB.Bson.Serialization.IBsonDocumentSerializer'. Source=MongoDB.Driver StackTrace: at MongoDB.Driver.Linq.Linq3Implementation.Translators.TranslationContext.Create(Expression expression, IBsonSerializer serializer, TranslationContextData data) at MongoDB.Driver.Linq.Linq3Implementation.LinqProviderAdapterV3.TranslateExpressionToFilter[TDocument](Expression`1 expression, IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry) at MongoDB.Driver.ExpressionFilterDefinition`1.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) at MongoDB.Driver.PullUpdateDefinition`2.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider) at MongoDB.Driver.MongoCollectionImpl`1.ConvertWriteModelToWriteRequest(WriteModel`1 model, Int32 index) at System.Linq.Enumerable.<SelectIterator>d__229`2.MoveNext() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at MongoDB.Driver.Core.Operations.BulkMixedWriteOperation..ctor(CollectionNamespace collectionNamespace, IEnumerable`1 requests, MessageEncoderSettings messageEncoderSettings) at MongoDB.Driver.MongoCollectionImpl`1.CreateBulkWriteOperation(IClientSessionHandle session, IEnumerable`1 requests, BulkWriteOptions options) at MongoDB.Driver.MongoCollectionImpl`1.<BulkWriteAsync>d__33.MoveNext() at MongoDB.Driver.MongoCollectionImpl`1.<UsingImplicitSessionAsync>d__115`1.MoveNext() at MongoDB.Driver.MongoCollectionBase`1.<UpdateOneAsync>d__113.MoveNext() at Program.<<Main>$>d__0.MoveNext() in C:\Users\jn\source\repos\MongoDbLinqProvider3PullProblem\MongoDbLinqProvider3PullFromGuidArrayProblem\Program.cs:line 40 at Program.<Main>(String[] args)

Demonstrated by this code:

using MongoDB.Driver; using MongoDB.Driver.Linq; var settings = new MongoClientSettings { LinqProvider = LinqProvider.V3 }; var client = new MongoClient(settings); var db = client.GetDatabase("test"); var coll = db.GetCollection<Foo>("Foos"); var fooId = Guid.Parse("{9DF3D93D-9989-42BB-B54E-A3AB603CE05E}"); var barId1 = Guid.Parse("{B1554855-6B5A-4AD5-AE2F-33567A057A39}"); var barId2 = Guid.Parse("{90D62CD4-12DF-49D9-A3C9-62C00EFAB23A}"); var foo = new Foo { Id = fooId, BarRefs = { barId1, barId2, }, }; var barRefsToRemove = new List<Guid> { barId1, }; var fooIdFilter = Builders<Foo>.Filter.Eq(x => x.Id, fooId); await coll.DeleteOneAsync(fooIdFilter); await coll.InsertOneAsync(foo); //var update = Builders<Foo>.Update.Pull(x => x.BarRefs, barId1); var update = Builders<Foo>.Update.PullFilter(x => x.BarRefs, y => barRefsToRemove.Contains(y)); var query = await coll.UpdateOneAsync(fooIdFilter, update); record Foo { public Guid Id { get; set; } public List<Guid> BarRefs { get; set; } = []; };

Using Pull (singled value instead of filter), works fine (commented out).

So does wrapping the Guids in a wrapper class, presumably because the array is then a document array instead of a Guid array:

using MongoDB.Driver; using MongoDB.Driver.Linq; var settings = new MongoClientSettings { LinqProvider = LinqProvider.V3 }; var client = new MongoClient(settings); var db = client.GetDatabase("test"); var coll = db.GetCollection<Foo>("Foos"); var fooId = Guid.Parse("{9DF3D93D-9989-42BB-B54E-A3AB603CE05E}"); var barId1 = Guid.Parse("{B1554855-6B5A-4AD5-AE2F-33567A057A39}"); var barId2 = Guid.Parse("{90D62CD4-12DF-49D9-A3C9-62C00EFAB23A}"); var foo = new Foo { Id = fooId, BarRefs = { GuidIdWrapper.FromId(barId1), GuidIdWrapper.FromId(barId2), }, }; var barRefsToRemove = new List<Guid> { barId1, }; var fooIdFilter = Builders<Foo>.Filter.Eq(x => x.Id, fooId); await coll.DeleteOneAsync(fooIdFilter); await coll.InsertOneAsync(foo); var update = Builders<Foo>.Update.PullFilter(x => x.BarRefs, y => barRefsToRemove.Contains(y.Id)); var query = await coll.UpdateOneAsync(fooIdFilter, update); record Foo { public Guid Id { get; set; } public List<GuidIdWrapper> BarRefs { get; set; } = []; }; class GuidIdWrapper { public Guid Id { get; set; } public static GuidIdWrapper FromId(Guid id) { return new GuidIdWrapper { Id = id, }; } }

Would of course rather avoid having to migrate existing documents to accommodate this.