simple-odata-client/Simple.OData.Client

Client fails to handle IDictionary implementations

cdavernas opened this issue · 0 comments

What happens?

The OData client fails to properly handle non-default implementation of the IDictionary interface

What do you expect to happen?

The OData client to properly handle non-default implementation of the IDictionary interface

How to reproduce?

  1. Create a new dictionary implementation, such as the following:
public class NameValueCollection<T>
  : IDictionary<string, T>, ICollection
  {
     //...

     public NameValueCollection()
     {
        //...
     }

     public NameValueCollection(IEnumerable<KeyValuePair<string, T>> source)
     {
        //...
     }

     //..
  }
  1. Add a NameValueCollection property to one of the models exposed on OData:
public class Book
{

   //...

  public string Title { get; set;}
  
  public string Author { get; set; } 

  public NameValueCollection<string> Metadata { get; set; }

   //...

}
  1. Make an OData request to list books via the Simple.OData.Client

Additional information

Stack trace:

Simple.OData.Client.Extensions.ConstructorNotFoundException: Could not find a constructor for type 'NameValueCollection`1' with the following argument types: IEnumerable`1
   at Simple.OData.Client.Extensions.ExpressionActivator.CreateActivator(Type type, Type[] parameters)
   at Simple.OData.Client.Extensions.DictionaryExtensions.<>c__DisplayClass16_0.<ConvertCollection>b__0(Tuple`2 t)
   at System.Collections.Concurrent.ConcurrentDictionary`2[[System.Tuple`2[[System.Type, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Type, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Simple.OData.Client.Extensions.ActivatorDelegate, Simple.OData.Client.Core, Version=5.26.0.0, Culture=neutral, PublicKeyToken=58885f4495efc1ae]].GetOrAdd(Tuple`2 key, Func`2 valueFactory)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ConvertCollection(Type type, ITypeCache typeCache, Object itemValue)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ConvertValue(Type type, ITypeCache typeCache, Object itemValue)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ToObject(IDictionary`2 source, ITypeCache typeCache, Type type, Boolean dynamicObject)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ConvertSingle(Type type, ITypeCache typeCache, Object itemValue)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ConvertCollection(Type type, ITypeCache typeCache, Object itemValue)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ConvertValue(Type type, ITypeCache typeCache, Object itemValue)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ToObject(IDictionary`2 source, ITypeCache typeCache, Type type, Boolean dynamicObject)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ConvertSingle(Type type, ITypeCache typeCache, Object itemValue)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ConvertCollection(Type type, ITypeCache typeCache, Object itemValue)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ConvertValue(Type type, ITypeCache typeCache, Object itemValue)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ToObject(IDictionary`2 source, ITypeCache typeCache, Type type, Boolean dynamicObject)
   at Simple.OData.Client.Extensions.DictionaryExtensions.ToObject[V1Correlation](IDictionary`2 source, ITypeCache typeCache, Boolean dynamicObject)
   at Simple.OData.Client.FluentClientBase`2[[Synapse.Integration.Models.V1Correlation, Synapse.Integration, Version=0.1.25.0, Culture=neutral, PublicKeyToken=null],[Simple.OData.Client.IBoundClient`1[[Synapse.Integration.Models.V1Correlation, Synapse.Integration, Version=0.1.25.0, Culture=neutral, PublicKeyToken=null]], Simple.OData.Client.Core, Version=5.26.0.0, Culture=neutral, PublicKeyToken=58885f4495efc1ae]].ConvertResult(IDictionary`2 result, String dynamicPropertiesContainerName)
   at Simple.OData.Client.FluentClientBase`2.<>c__DisplayClass97_0[[Synapse.Integration.Models.V1Correlation, Synapse.Integration, Version=0.1.25.0, Culture=neutral, PublicKeyToken=null],[Simple.OData.Client.IBoundClient`1[[Synapse.Integration.Models.V1Correlation, Synapse.Integration, Version=0.1.25.0, Culture=neutral, PublicKeyToken=null]], Simple.OData.Client.Core, Version=5.26.0.0, Culture=neutral, PublicKeyToken=58885f4495efc1ae]].<FilterAndTypeColumnsAsync>b__0(IDictionary`2 z)
   at System.Linq.Utilities.<>c__DisplayClass2_0`3[[Simple.OData.Client.AnnotatedEntry, Simple.OData.Client.Core, Version=5.26.0.0, Culture=neutral, PublicKeyToken=58885f4495efc1ae],[System.Collections.Generic.IDictionary`2[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Synapse.Integration.Models.V1Correlation, Synapse.Integration, Version=0.1.25.0, Culture=neutral, PublicKeyToken=null]].<CombineSelectors>b__0(AnnotatedEntry x)

Note to implementers

The bug is due to an improper check in the DictionaryExtensions: I believe you should test there if the type passed as argument is an IDictionary implementation, in which case the element type should be KeyValuePair<T1, T2> instead of just T1.