[CSHARP-1423] System.ArgumentException in BsonMemberMap.SetSerializer when trying to apply $select Created: 24/Sep/15  Updated: 08/Dec/21  Resolved: 26/Nov/20

Status: Closed
Project: C# Driver
Component/s: Linq, Serialization
Affects Version/s: 2.1
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Joe Wang Assignee: James Kovacs
Resolution: Duplicate Votes: 9
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
is depended on by CSHARP-3989 oDATA $select support with LINQ3 Closed
Duplicate
duplicates CSHARP-1771 Support IIF method (i.e. ternary oper... Closed

 Description   

Using the ASP.NET WebApi 2 implementation of OData 3, I'm trying to apply the $select query option to the new IQueryable being returned from the new 2.1 RC driver. I keep running into this error when enumerating the IQueryable, however:

System.ArgumentException: Value type of serializer is System.Web.Http.OData.Query.Expressions.PropertyContainer+NamedProperty`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] and does not match member type System.Web.Http.OData.Query.Expressions.PropertyContainer.

Parameter name: serializer
at MongoDB.Bson.Serialization.BsonMemberMap.SetSerializer(IBsonSerializer serializer)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.BuildClassMap(Type type, ProjectionMapping mapping)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.BuildProjectedSerializer(ProjectionMapping mapping)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.BuildMemberInit(MemberInitExpression node)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.Build(Expression node)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.BuildClassMap(Type type, ProjectionMapping mapping)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.BuildClassMap(Type type, ProjectionMapping mapping)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.BuildClassMap(Type type, ProjectionMapping mapping)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.BuildProjectedSerializer(ProjectionMapping mapping)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.BuildMemberInit(MemberInitExpression node)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.Build(Expression node)
at MongoDB.Driver.Linq.Processors.SerializerBuilder.Build(Expression node, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.Linq.Processors.PipelineBindingContext.GetSerializer(Type type, Expression node)
at MongoDB.Driver.Linq.Processors.Pipeline.MethodCallBinders.SelectBinder.Bind(PipelineExpression pipeline, PipelineBindingContext bindingContext, MethodCallExpression node, IEnumerable`1 arguments)
at MongoDB.Driver.Linq.Processors.MethodInfoMethodCallBinder`1.Bind(PipelineExpression pipeline, TBindingContext bindingContext, MethodCallExpression node, IEnumerable`1 arguments)
at MongoDB.Driver.Linq.Processors.PipelineBinderBase`1.BindMethodCall(MethodCallExpression node)
at MongoDB.Driver.Linq.Processors.PipelineBinderBase`1.Bind(Expression node)
at MongoDB.Driver.Linq.Processors.Pipeline.PipelineBinder.Bind(Expression node, IBsonSerializer rootSerializer, IBsonSerializerRegistry serializerRegistry)
at MongoDB.Driver.Linq.MongoQueryProviderImpl`1.Prepare(Expression expression)
at MongoDB.Driver.Linq.MongoQueryProviderImpl`1.Translate(Expression expression)
at MongoDB.Driver.Linq.MongoQueryProviderImpl`1.Execute(Expression expression)
at MongoDB.Driver.Linq.MongoQueryableImpl`2.GetEnumerator()
at MongoDB.Driver.Linq.MongoQueryableImpl`2.System.Collections.IEnumerable.GetEnumerator()
at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, ODataWriter writer, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)
at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)

The IQueryable debug view shows this:

DebugView ".Call System.Linq.Queryable.Select(
.Constant<MongoDB.Driver.Linq.IMongoQueryable`1[MyObject]>(aggregate([])),
'(.Lambda #Lambda1<System.Func`2[MyObject,System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectSome`1[MyObject]]>))

.Lambda #Lambda1<System.Func`2[MyObject,System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectSome`1[MyObject]]>(MyObject $var1)
{
.New System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectSome`1[MyObject](){
ModelID = \"b5ae8d92-3fb0-4049-a9f2-c72805d87209\",
Container = .New System.Web.Http.OData.Query.Expressions.PropertyContainer+AutoSelectedNamedPropertyWithNext`1[System.String]()
{
Name = \"id\",
Value = $var1.id,
Next = .New System.Web.Http.OData.Query.Expressions.PropertyContainer+NamedProperty`1[System.String]()

{ Name = \"Name\", Value = $var1.Name }

}
}
}" string

So it seems like the value of Container (in System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectSome) is actually not PropertyContainer (which is what's defined in the class), but a SubClass of it, hence causing the exception to be thrown.

Might there be a way to resolve this issue in SetSerializer?



 Comments   
Comment by James Kovacs [ 26/Nov/20 ]

We have investigated this further and concluded that this issue is a duplicate of CSHARP-1771. OData makes heavy use of IIF statements when generating its expression trees. For example, take a simple Product model as follows:

    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

And the following ODataController method:

        [EnableQuery]
        public IQueryable<Product> Get()
        {
            return _coll.AsQueryable();
        }

Querying using the $select OData operator (https://example.com:8888/odata/Products?$select=Name) results in the MongoDB LINQ provider receiving the following expression tree:

aggregate([]).Select($it => new SelectSome`1()
{
    ModelID = value(Microsoft.AspNet.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.String]).TypedProperty, 
    Container = new NamedPropertyWithNext0`1()
    {
        Name = "Name",
        Value = IIF(($it == null), null, $it.Name),
        Next0 = new AutoSelectedNamedProperty`1()
        {
            Name = "Id", Value = IIF(($it == null), null, Convert($it.Id, Nullable`1))
        }
    }
})

Our current LINQ provider does not support LINQ's IIF method, which is why $select (and $expand) in OData fails.

We are currently working on a new implementation of our LINQ provider and will make sure IIF works properly in the new implementation. This should allow $select and $expand to work correctly.

Since the root cause of this issue and CSHARP-1771 are identical, we have closed this issue as a duplicate. Please follow CSHARP-1771 for updates on IIF support in our new LINQ implementation.

Comment by Jeffrey Yemin [ 20/Nov/20 ]

Sorry for the radio silence on this. We hear you. We're going to re-triage this issue and see if we can come up with a solution. Meanwhile if anyone has any ideas on how to fix it, please share them.

Comment by Dimitri Kroo [ 20/Nov/20 ]

Still any plans to fix it?

Comment by Diego Dalben [ 12/Jun/20 ]

In June 2020 this error still occurs.

Is there a correction forecast?

Comment by Yan Wenwei [ 01/Mar/20 ]

It's the year 2020, I am using oData 7.3.0 and MongoDb.Driver 2.10.0. And this issue still exists. 

Do you have any plans?

Comment by Martin Opdenacker [ 15/Nov/19 ]

When can we expect this issue to be resolved ? We really need this.

Comment by Joseph Washington [ 21/Nov/18 ]

select and expand do not work for me - Microsoft.AspNetCore.OData 7.1.0, MongoDB.Driver 2.7.2 - Has anyone found a work around?

Comment by Ross Buggins [ 29/Oct/18 ]

I can confirm that all the other operations still  seem to work (filter, order etc) just select that wont. Ross

Comment by Ross Buggins [ 29/Oct/18 ]

Still an issue in latest .net and latest mongodb driver - any thoughts on this one at all? 

Ross

Comment by Christopher Florencia [ 20/Apr/18 ]

The issue is still present with Driver v2.5.1 and Microsoft.AspNetCore.Odata 7.0.0.beta2 (internally Micorosft.Odata.Core 7.3.1)

Christopher.

Comment by Steve Craze [ 25/Jan/18 ]

Hi,
Is there any further news on this or any way to work around it?
Steve

Comment by Semyon Ivanov [ 27/Sep/16 ]

The issue is still present with Driver v2.3.0 and OData v4 (6.0.0)

Comment by Craig Wilson [ 05/Feb/16 ]

Hi Johan,

I'm not completely positive this is our issue. OData does some very interesting things with an expression tree which require us to handle special OData specific types. We don't have any current plans to handle this. I'll bring it up in our next planning meeting.

Craig

Comment by Johan Hammar [ 05/Feb/16 ]

As of the current version 2.2.2.10 this issue remains. Until this has been fixed MongoDB cannot be used as a backend to Azure Mobile service (or Mobile App) clients that uses offline sync since it relies on OData queries on the server side.

Is there any update on when this will be fixed?

Comment by Craig Wilson [ 25/Sep/15 ]

Ok. Thanks for the report. I don't think this is going to get fixed for 2.1. This is going to be much more involved than I thought. I put a fix for 2.2, but might get it into a 2.1 point release.

Comment by Joe Wang [ 25/Sep/15 ]

Yep--it only happens for $select – all the other OData operators seem to work fine!

Comment by Craig Wilson [ 25/Sep/15 ]

Yeah... Somehow, these are going to have to get translated into an actual POCO object before getting passed to our LINQ provider. We don't know anything about these special types. This only happens for $select? Does OData get translated ok for $filter, $skip, etc...?

Comment by Joe Wang [ 25/Sep/15 ]

The OData project I have is fairly simple – here's an example of what I'm doing:

class MyObject 
{
	public string id { get; set; }
	public string Name { get; set; }
}

in the controller:

public Task<IQueryable> Get(ODataQueryOptions<MyObject> queryOptions)
{
	// ... code to get the Mongo collection ...
	return queryOptions.ApplyTo(mongoCollection.AsQueryable<MyObject>(), new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.False });
}

From digging around the OData code, the Select expression (say for selecting MyObject.Name) it appears to build is as follows:

mongoQueryable.Select(var => new SelectExpandWrapper { Instance = "SomeString", ModelID = "SomeString", TypeName = "SomeString", Container = new AutoSelectedNamedPropertyWithNext<string> { Name = "SomeString", Value = var.id, AutoSelected = true, Next = new NamedProperty<string> { Name = "Name", Value = var.Name } } });

where

class SelectExpandWrapper
{
	public string Instance { get; set; }
	public string ModelID { get; set; }
	public string TypeName { get; set; }
	public PropertyContainer Container { get; set; }
}
 
abstract class PropertyContainer
{
}
 
class NamedProperty<T> : PropertyContainer
{
	public string Name { get; set; }
	public T Value { get; set;}
}
 
class AutoSelectedNamedPropertyWithNext<T> : NamedProperty<T>
{
	public bool AutoSelected { get; set; }
	public PropertyContainer Next { get; set; }
}

(I've abbreviated the classes here, full source at https://github.com/OData/WebApi/tree/master/OData/src/System.Web.Http.OData/OData/Query/Expressions)

Comment by Craig Wilson [ 25/Sep/15 ]

The SelectBinder supports a lot of things, all related to either a POCO or a BsonDocument. What the above looks like is something else which needs special interpretation. I haven't done any work on integrating with OData yet. It might be that we need to do some special mapping for OData to work correctly given that it's generating naming containers and what not...

Would you mind posting a sample OData project so I can see how you are using OData.

Comment by Joe Wang [ 25/Sep/15 ]

Also, it seems like the Select Expression being generated is something along the lines of

MongoQueryable.Select(var => new { Name = "id", Value = var.id, Next = new { Name = "Name", Value = var.Name, Next = new

{...}

} })

which the current SelectBinder doesn't support.

Comment by Joe Wang [ 25/Sep/15 ]

Just to see what would happen, I took a local build and commented out the check in SetSerializer – it goes further, but throws requiring that the LambdaExpression.Name be populated – and it seems like the behavior of the OData library currently does not do this

Comment by Craig Wilson [ 24/Sep/15 ]

No, I don't think this is something to do in SetSerializer. But I may be able to do something in LINQ. I'll take a look. Thanks for reporting.

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