[CSHARP-1751] Generic Collection throws "{document} is not supported." on ReplaceOneAsync Created: 20/Aug/16  Updated: 22/Aug/16  Resolved: 22/Aug/16

Status: Closed
Project: C# Driver
Component/s: API, Operations
Affects Version/s: 2.2.4
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Tseng [X] Assignee: Unassigned
Resolution: Duplicate Votes: 0
Labels: driver, query
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

OS: Windows 10, MongoDB: 3.2.9 (Windows Server 2008 R2 with SSL)


Issue Links:
Duplicate
duplicates CSHARP-1240 Type name store in the _t field missi... Closed

 Description   

When trying to add a generic class to the collection, MongoDB CSharp Driver throws an System.AggregateException exception.

System.AggregateException was unhandled by user code
  HResult=-2146233088
  Message=Mindestens ein Fehler ist aufgetreten.
  Source=mscorlib
  StackTrace:
       bei System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
       bei System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
       bei System.Threading.Tasks.Task`1.get_Result()
       bei Dealz.ProductListing.Data.MongoDb.Repositories.TypedIndexRepository.<>c__1`1.<AddIndexAsync>b__1_0(Task`1 result) in D:\Projekte\CSharp\Dealz\src\Dealz.ProductListing.Data.MongoDb\Repositories\TypedIndexRepository.cs:Zeile 41.
       bei System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
       bei System.Threading.Tasks.Task.Execute()
  InnerException: 
       HResult=-2146233079
       Message={document} is not supported.
       Source=MongoDB.Driver
       StackTrace:
            bei MongoDB.Driver.Linq.Translators.PredicateTranslator.GetFieldExpression(Expression expression)
            bei MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateTypeComparisonQuery(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)
            bei MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateComparison(BinaryExpression binaryExpression)
            bei MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node)
            bei MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateAndAlso(BinaryExpression node)
            bei MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node)
            bei MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node, IBsonSerializerRegistry serializerRegistry)
            bei MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate[TDocument](Expression`1 predicate, IBsonSerializer`1 parameterSerializer, IBsonSerializerRegistry serializerRegistry)
            bei MongoDB.Driver.ExpressionFilterDefinition`1.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
            bei MongoDB.Driver.MongoCollectionImpl`1.ConvertWriteModelToWriteRequest(WriteModel`1 model, Int32 index)
            bei System.Linq.Enumerable.<SelectIterator>d__5`2.MoveNext()
            bei MongoDB.Driver.Core.Operations.BulkMixedWriteOperation.BatchHelper.<FindOrderedRuns>d__8.MoveNext()
            bei MongoDB.Driver.Core.Misc.ReadAheadEnumerable`1.ReadAheadEnumerator.MoveNext()
            bei MongoDB.Driver.Core.Operations.BulkMixedWriteOperation.BatchHelper.<GetBatches>d__6.MoveNext()
            bei MongoDB.Driver.Core.Operations.BulkMixedWriteOperation.<ExecuteAsync>d__39.MoveNext()
         --- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
            bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
            bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
            bei MongoDB.Driver.OperationExecutor.<ExecuteWriteOperationAsync>d__3`1.MoveNext()
         --- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
            bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
            bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
            bei MongoDB.Driver.MongoCollectionImpl`1.<ExecuteWriteOperationAsync>d__61`1.MoveNext()
         --- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
            bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
            bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
            bei MongoDB.Driver.MongoCollectionImpl`1.<BulkWriteAsync>d__22.MoveNext()
         --- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
            bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
            bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
            bei MongoDB.Driver.MongoCollectionBase`1.<ReplaceOneAsync>d__39.MoveNext()
       InnerException: 

I am using the following Model for the collection

IndexedItem.cs

   public class IndexedItem
    {
        protected IndexedItem() { }
 
        public IndexedItem(string key, string value)
        {
            Key = key;
            Value = value;
        }
 
        public IndexedItem(string key, string value, string displayText) : this(key, value)
        {
            DisplayText = displayText;
        }
 
        public string Key { get; set; }
        public string Value { get; set; }
        public string DisplayText { get; set; }
    }
 
    public class IndexedItem<T> : IndexedItem
    {
        public IndexedItem() : base()
        {
            Type = typeof(T).FullName;
        }
 
        public IndexedItem(string key, string value) : this()
        {
            Key = key;
            Value = value;
        }
 
        public IndexedItem(string key, string value, string displayText) : this(key, value)
        {
            DisplayText = displayText;
        }
 
        public string Type { get; }
    }

TypedIndexedItemRepository.cs

       public async Task<QueryResult> AddIndexAsync<T>(IndexedItem<T> item, CancellationToken cancellationToken = default(CancellationToken))
        {
            var indexCollection = productsDatabase.GetCollection<IndexedItem<T>>(IndexCollection);
 
            var result = await indexCollection.ReplaceOneAsync(
                i => i.GetType() == typeof(IndexedItem<T>) && i.Key == item.Key,
                item,
                new UpdateOptions
                {
                    IsUpsert = true
                },
                cancellationToken: cancellationToken
            );
 
            if (result.IsAcknowledged && result.ModifiedCount > 0)
                return QueryResult.Success;
 
            return QueryResult.Failed();
        }

The idea is to have *one* collection, to hold indexed item for multiple types, i.e. IndexedItem<User> to index the user (i.e. for AutoComplete functionality).

Expected result:

{
  "_t": "Dealz.ProductListings.Contracts.Tag, Dealz.ProductListings",
  "key" : "tagkey", 
  "value": "tagname",
  "displayText": "Some Name"
}



 Comments   
Comment by Tseng [X] [ 22/Aug/16 ]

Sure, can be closed. The workaround should allow me to work past the error. It was blocking me for days already

Comment by Craig Wilson [ 22/Aug/16 ]

I forgot about that problem. What you are now hitting is an existing bug (CSHARP-1240). We don't have a solution for this, as changing it now could be hugely backwards breaking. I've just though of a workaround that I'll post in the other ticket, but I believe that we are now done with this current issue and have moved to that one.

Let me know if it's ok to close this ticket and start discussing options in the other.
Craig

Comment by Tseng [X] [ 22/Aug/16 ]

Thanks, very helpful.

This brought me a little further. But the inserted type has (viewed in MongoDB Management Studio)

{
	"_id": ObjectId('57bb1ea139a1b2521e525473'),
	"_t": "IndexedItem`1",
	"Key": "clothes",
	"Value": "clothes",
	"DisplayText": null
}

The "_t" value shows IndexedItem`1 which seems wrong, it only has the name of the generic, but not the generic args in it and it's not full qualified name. I'd expected something like "Project.Models.IndexedItems<Project.Models.Tag>, Project.Contracts" where "Project.Contracts" is the assembly name.

How would the deserializes know to which class to cast it back then without the generic args or the full qualified name?

Comment by Craig Wilson [ 22/Aug/16 ]

I believe it's the code i.GetType() == typeof(IndexedItem<T>) that isn't working. I believe you can change the check to an "is" and it should work.

i => i is IndexedItem<T> && i.Key == itemKey

I'm unclear as to whether you are storing the discriminator manually or letting us do it. If you are letting us do it, you can use the OfType method before ReplaceOneAsync. Something like this:

var collection = database.GetCollection<IndexedItem>("items");
 
// OfType will add the filter to your query automatically.
collection.OfType<IndexedItem<T>>().ReplaceOneAsync(i => i.Key == item.Key,
                item,
                new UpdateOptions
                {
                    IsUpsert = true
                },
                cancellationToken: cancellationToken
            );

Craig

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