[CSHARP-4502] UpdateMany call fails after updating from 2.18 to 2.19 Created: 30/Jan/23  Updated: 17/Feb/23  Resolved: 17/Feb/23

Status: Closed
Project: C# Driver
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: J.D. Robertson Assignee: Robert Stam
Resolution: Duplicate Votes: 2
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
duplicates CSHARP-4499 Support Convert calls to a base type ... Closed
is duplicated by CSHARP-4501 Updating from version 2.18 to 2.19 of... Closed
Documentation Changes Summary:

1. What would you like to communicate to the user about this feature?
2. Would you like the user to see examples of the syntax and/or executable code and its output?
3. Which versions of the driver/connector does this apply to?


 Description   

Summary

We have a helper library on top of the driver that wraps calls to UpdateManyAsync . I will include pseudocode in the following section. All of our consumers of this library are breaking if we try and update from version 2.18 to 2.19. As I am not expecting a compatibility breaking change in a minor version update I am posting an issue to see if this is an unexpected regression.

How to Reproduce

The following code shows what we are doing.

public async Task MarkAllAsDeleted(Expression<Func<MyEntry, bool>> filter)
{
   // Define changes to apply to the database.
   var changes = new List<Tuple<Expression<Func<MyEntry, object>>, object>>();
   changes.Add(new Tuple<Expression<Func<AddressGroupEntry, object>>, object (entry => entry.Deleted, true));
 
   var mongoUpdateDefinition = ToMongoUpdateDefinition(changes);   
 
   var collection = Context.Database.GetCollection<MyEntry>(CollectionName);
   await Collection.UpdateManyAsync(filter, mongoUpdateDefinition);
   // Result handling omitted
}
 
public static UpdateDefinition<TEntry> ToMongoUpdateDefinition<TEntry>(List<Tuple<Expression<Func<MyEntry, object>>, object>> changes)
{
  var updates = new List<UpdateDefinition<TEntry>>();
  foreach (var change in changes)
  {
    updates.Add(Builders<TEntry>.Update.Set(change.Item1 as dynamic, change.Item2 as dynamic));
  }
  return Builders<TEntry>.Update.Combine(updates);
}

After updating to 2.19, the above code generated a callstack like:

MongoDB.Driver.Linq.ExpressionNotSupportedException: Expression not supported: Convert(entry.Deleted, Object).\n at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators.ConvertExpressionToFilterFieldTranslator.Translate(TranslationContext context, UnaryExpression expression)\n at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators.ExpressionToFilterFieldTranslator.Translate(TranslationContext context, Expression expression)\n at MongoDB.Driver.Linq.Linq3Implementation.LinqProviderAdapterV3.TranslateExpressionToField[TDocument,TField](Expression\u00601 expression, IBsonSerializer\u00601 documentSerializer, IBsonSerializerRegistry serializerRegistry, Boolean allowScalarValueForArrayField)\n at MongoDB.Driver.ExpressionFieldDefinition\u00602.Render(IBsonSerializer\u00601 documentSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider, Boolean allowScalarValueForArrayField)\n at MongoDB.Driver.ExpressionFieldDefinition\u00602.Render(IBsonSerializer\u00601 documentSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider)\n at MongoDB.Driver.OperatorUpdateDefinition\u00602.Render(IBsonSerializer\u00601 documentSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider)\n at MongoDB.Driver.CombinedUpdateDefinition\u00601.Render(IBsonSerializer\u00601 documentSerializer, IBsonSerializerRegistry serializerRegistry, LinqProvider linqProvider)\n at MongoDB.Driver.MongoCollectionImpl\u00601.ConvertWriteModelToWriteRequest(WriteModel\u00601 model, Int32 index)\n at System.Linq.Enumerable.SelectIterator[TSource,TResult](IEnumerable\u00601 source, Func\u00603 selector)\u002BMoveNext()\n at System.Collections.Generic.List\u00601..ctor(IEnumerable\u00601 collection)\n at System.Linq.Enumerable.ToList[TSource](IEnumerable\u00601 source)\n at MongoDB.Driver.Core.Operations.BulkMixedWriteOperation..ctor(CollectionNamespace collectionNamespace, IEnumerable\u00601 requests, MessageEncoderSettings messageEncoderSettings)\n at MongoDB.Driver.MongoCollectionImpl\u00601.CreateBulkWriteOperation(IClientSessionHandle session, IEnumerable\u00601 requests, BulkWriteOptions options)\n at MongoDB.Driver.MongoCollectionImpl\u00601.BulkWriteAsync(IClientSessionHandle session, IEnumerable\u00601 requests, BulkWriteOptions options, CancellationToken cancellationToken)\n at MongoDB.Driver.MongoCollectionImpl\u00601.UsingImplicitSessionAsync[TResult](Func\u00602 funcAsync, CancellationToken cancellationToken)\n at MongoDB.Driver.MongoCollectionBase\u00601.UpdateManyAsync(FilterDefinition\u00601 filter, UpdateDefinition\u00601 update, UpdateOptions options, Func\u00603 bulkWriteAsync)\n at NationalInstruments.NotificationService.DataStore.Providers.Mongo.MongoDataProvider\u00601.UpdateAsync(Expression\u00601 match, Changes\u00601 changes) in /app/NotificationService/NotificationService.DataStore/Providers/Mongo/MongoDataProvider.cs:line 290\n

Additional Background



 Comments   
Comment by Robert Stam [ 17/Feb/23 ]

Closing this ticket as a duplicate of CSHARP-4499.

Comment by J.D. Robertson [ 10/Feb/23 ]

It does very much sound like the same issue as that other bug report. We also have very generic code that does not know the type of the fields it is interacting with and so boxes the types in an object. Which works in LINQ2 but seems not to work in LINQ3.

I don't have an ETA on when I can produce a standalone reproducing case. I have several higher priorities to look at right now.

Comment by Robert Stam [ 10/Feb/23 ]

Based on the error message and the stack trace I suspect this is the same underlying issue as CSHARP-4499.

If I can get the provided code to compile I can confirm that.

Comment by Robert Stam [ 10/Feb/23 ]

I still don't seem to have enough information to reproduce this.

It would help to have the class definitions for `MyEntry` and `AddressGroupEntry`.

The call to `changes.Add` in `DeleteAllThatMatch` doesn't compile because the `Tuple` types don't mach.

Could you please provide a full example that isn't missing anything and that compiles without errors that I could use to reproduce this with?

Thanks. 

Comment by Alexandre Junior [ 06/Feb/23 ]

Got the same bug, broke down my application.

I've filed an issue with a similar repro: https://jira.mongodb.org/browse/CSHARP-4519

Comment by J.D. Robertson [ 30/Jan/23 ]

Apologies, should be:

public async Task DeleteAllThatMatch(IMongoDataContext context, Expression<Func<MyEntry, bool>> filter){
   // Define changes to apply to the database.
   var changes = new List<Tuple<Expression<Func<MyEntry, object>>, object>>();
  changes.Add(new Tuple<Expression<Func<AddressGroupEntry, object>>, object (entry => entry.Deleted, true));
			
   var mongoUpdateDefinition = ToMongoUpdateDefinition(changes);   
			
   var collection = context.Database.GetCollection<MyEntry>(CollectionName); // Context is an 
   await collection.UpdateManyAsync(filter, mongoUpdateDefinition);
    // Result handling omitted
}public static UpdateDefinition<TEntry> ToMongoUpdateDefinition<TEntry>(IList<Tuple<Expression<Func<TEntry, object>>, object>> changes)
{
       var updates = new List<UpdateDefinition<TEntry>>();
       foreach (var change in changes)
       {
            updates.Add(Builders<TEntry>.Update.Set(change.Item1 as dynamic, change.Item2 as dynamic));
       }
       return Builders<TEntry>.Update.Combine(updates); }

It may be easier to strip out the generic:

public async Task DeleteAllThatMatch(IMongoDataContext context, Expression<Func<MyEntry, bool>> filter)
{ 
   // Define changes to apply to the database.  
   var changes = new List<Tuple<Expression<Func<MyEntry, object>>, object>>();  
   changes.Add(new Tuple<Expression<Func<AddressGroupEntry, object>>, object (entry => entry.Deleted, true)); 
   var mongoUpdateDefinition = ToMongoUpdateDefinition(changes);    
   var collection = context.Database.GetCollection<MyEntry>(CollectionName); 
   await collection.UpdateManyAsync(filter, mongoUpdateDefinition);  
   // Result handling omitted 
}
 
public static UpdateDefinition<MyEntry> ToMongoUpdateDefinition(IList<Tuple<Expression<Func<MyEntry, object>>, object>> changes)
{
   var updates = new List<UpdateDefinition<MyEntry>>();
   foreach (var change in changes)
   {
     updates.Add(Builders<MyEntry>.Update.Set(change.Item1 as dynamic, change.Item2 as dynamic));
   }
   return Builders<MyEntry>.Update.Combine(updates);
}

 

 

Comment by Robert Stam [ 30/Jan/23 ]

Thank you for reporting this issue. My apologies that you have been affected.

I'm trying to reproduce this but can't get a test to compile. There seems to be at least one mismatched angle brackets, and I can't figure out the relationship between TEntry, MyEntry and AddressGroupEntry.

As soon as I receive a bit more information I will continue my attempt to reproduce this.

You can see my initial attempt to reproduce this here:

https://github.com/rstam/mongo-csharp-driver/tree/csharp4502

Generated at Wed Feb 07 21:48:25 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.