Skip to content

Commit

Permalink
Fix the issue of materializing collection of complex type
Browse files Browse the repository at this point in the history
  • Loading branch information
LaylaLiu committed Feb 1, 2016
1 parent ccea67e commit 883f5a6
Show file tree
Hide file tree
Showing 15 changed files with 2,303 additions and 28 deletions.
7 changes: 3 additions & 4 deletions src/Microsoft.OData.Client/ClientEdmModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ internal IEdmType GetOrCreateEdmType(Type type)
}

Debug.Assert(cachedEdmType != null, "cachedEdmType != null");
this.ValidateComplexTypeHasProperties(type, cachedEdmType);
this.ValidateComplexType(type, cachedEdmType);
return cachedEdmType.EdmType;
}

Expand Down Expand Up @@ -334,11 +334,10 @@ private static Type[] GetTypeHierarchy(Type type, out PropertyInfo[] keyProperti
/// </summary>
/// <param name="type">The type in question</param>
/// <param name="cachedEdmType">The EdmTypeCacheValue of the type in question.</param>
private void ValidateComplexTypeHasProperties(Type type, EdmTypeCacheValue cachedEdmType)
private void ValidateComplexType(Type type, EdmTypeCacheValue cachedEdmType)
{
Debug.Assert(cachedEdmType != null, "cachedEdmType != null");

// Note that if the type is an entity type, it has at least the key properties, thus there is no need to validate it here.
if (cachedEdmType.EdmType.TypeKind == EdmTypeKind.Complex)
{
bool? hasProperties = cachedEdmType.HasProperties;
Expand All @@ -353,7 +352,7 @@ private void ValidateComplexTypeHasProperties(Type type, EdmTypeCacheValue cache
}
}

if (hasProperties == false)
if (hasProperties == false && (type == typeof(System.Object) || type.IsGenericType()))
{
throw c.Error.InvalidOperation(c.Strings.ClientType_NoSettableFields(type.ToString()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ internal void ApplyCollectionDataValues(
Debug.Assert(collectionProperty.Value != null, "Collection should have already been checked for nullness");
Debug.Assert(collectionInstance != null, "collectionInstance != null");
Debug.Assert(WebUtil.IsCLRTypeCollection(collectionInstance.GetType(), this.materializerContext.Model), "collectionInstance must be a CollectionValue");
Debug.Assert(
ClientTypeUtil.GetImplementationType(collectionInstance.GetType(), typeof(ICollection<>)).GetGenericArguments()[0] == collectionItemType,
Debug.Assert(collectionItemType.IsAssignableFrom(
ClientTypeUtil.GetImplementationType(collectionInstance.GetType(), typeof(ICollection<>)).GetGenericArguments()[0]),
"collectionItemType has to match the collectionInstance generic type.");
Debug.Assert(!ClientTypeUtil.TypeIsEntity(collectionItemType, this.materializerContext.Model), "CollectionValues cannot contain entities");
Debug.Assert(addValueToBackingICollectionInstance != null, "AddValueToBackingICollectionInstance != null");
Expand Down Expand Up @@ -178,8 +178,8 @@ internal void ApplyCollectionDataValues(
{
Debug.Assert(collectionInstance != null, "collectionInstance != null");
Debug.Assert(WebUtil.IsCLRTypeCollection(collectionInstance.GetType(), this.materializerContext.Model), "collectionInstance must be a CollectionValue");
Debug.Assert(
ClientTypeUtil.GetImplementationType(collectionInstance.GetType(), typeof(ICollection<>)).GetGenericArguments()[0] == collectionItemType,
Debug.Assert(collectionItemType.IsAssignableFrom(
ClientTypeUtil.GetImplementationType(collectionInstance.GetType(), typeof(ICollection<>)).GetGenericArguments()[0]),
"collectionItemType has to match the collectionInstance generic type.");
Debug.Assert(!ClientTypeUtil.TypeIsEntity(collectionItemType, this.materializerContext.Model), "CollectionValues cannot contain entities");
Debug.Assert(addValueToBackingICollectionInstance != null, "AddValueToBackingICollectionInstance != null");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ internal static object MaterializeODataEnumValue(Type enumType, ODataEnumValue e
{
// TODO: Find better way to parse Enum
string enumValueStr = enumValue.Value.Trim();
if (!Enum.IsDefined(enumType, enumValueStr))
Type underlyingType = Nullable.GetUnderlyingType(enumType) ?? enumType;
if (!Enum.IsDefined(underlyingType, enumValueStr))
{
tmpValue = Enum.Parse(enumType, ClientTypeUtil.GetClientFieldName(enumType, enumValueStr), false);
tmpValue = Enum.Parse(underlyingType, ClientTypeUtil.GetClientFieldName(underlyingType, enumValueStr), false);
}
else
{
tmpValue = Enum.Parse(enumType, enumValueStr, false);
tmpValue = Enum.Parse(underlyingType, enumValueStr, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ protected override void ReadWithExpectedType(IEdmTypeReference expectedClientTyp
collectionICollectionType = typeof(ICollection<>).MakeGenericType(new Type[] { collectionItemType });
}

Type clrCollectionType = WebUtil.GetBackingTypeForCollectionProperty(collectionICollectionType, collectionItemType);
Type clrCollectionType = WebUtil.GetBackingTypeForCollectionProperty(collectionICollectionType);
object collectionInstance = this.CollectionValueMaterializationPolicy.CreateCollectionInstance((IEdmCollectionTypeReference)expectedClientType, clrCollectionType);

// Enumerator over our collection reader was created, then ApplyDataCollections was refactored to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ protected override void ReadWithExpectedType(IEdmTypeReference expectedClientTyp
// We are here for two cases:
// (1) Something like Execute<ICollection<T>>, in which case the underlyingExpectedType is ICollection<T>
// (2) Execute<T> with the bool singleValue = false, in which case underlyingExpectedType is T
Type collectionItemType = underlyingExpectedType;
Type collectionItemType = this.ExpectedType;
Type collectionICollectionType = ClientTypeUtil.GetImplementationType(underlyingExpectedType, typeof(ICollection<>));
object collectionInstance;

Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OData.Client/TypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ internal ClientTypeAnnotation ResolveTypeForMaterialization(Type expectedType, s
collectionElementType = this.ResolveTypeForMaterialization(collectionElementType, collectionItemTypeName).ElementType;
}

Type clrCollectionType = WebUtil.GetBackingTypeForCollectionProperty(expectedType, collectionElementType);
Type clrCollectionType = WebUtil.GetBackingTypeForCollectionProperty(expectedType);
return this.clientEdmModel.GetClientTypeAnnotation(clrCollectionType);
}

Expand Down
6 changes: 2 additions & 4 deletions src/Microsoft.OData.Client/WebUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,11 @@ internal static string GetCollectionItemWireTypeName(string wireTypeName)
/// Resolves and creates if necessary a backing type for the <paramref name="collectionPropertyType"/>.
/// </summary>
/// <param name="collectionPropertyType">Type of a collection property as defined by the user - can be just an interface or generic type.</param>
/// <param name="collectionItemType">Type of items stored in the collection.</param>
/// <returns>Resolved concrete type that can be instantiated and will back the collection property. Can be the <paramref name="collectionPropertyType"/> type.</returns>
internal static Type GetBackingTypeForCollectionProperty(Type collectionPropertyType, Type collectionItemType)
internal static Type GetBackingTypeForCollectionProperty(Type collectionPropertyType)
{
Debug.Assert(collectionPropertyType != null, "collectionPropertyType != null");
Debug.Assert(ClientTypeUtil.GetImplementationType(collectionPropertyType, typeof(ICollection<>)) != null, "The type backing a collection has to implement ICollection<> interface.");
Debug.Assert(collectionItemType != null, "collectionItemType != null");

Type collectionBackingType = null;

Expand All @@ -183,7 +181,7 @@ internal static Type GetBackingTypeForCollectionProperty(Type collectionProperty
// Note that we don't check here if the type we created can be assigned to the user's type. This should be done by the caller (if requested)
if (collectionPropertyType.IsInterface())
{
collectionBackingType = typeof(ObservableCollection<>).MakeGenericType(collectionItemType);
collectionBackingType = typeof(ObservableCollection<>).MakeGenericType(collectionPropertyType.GetGenericArguments()[0]);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,9 @@ internal object ReadCollectionItem(IEdmTypeReference expectedItemTypeReference,
expectedItemTypeReference == null ||
expectedItemTypeReference.IsODataPrimitiveTypeKind() ||
expectedItemTypeReference.IsODataComplexTypeKind() ||
expectedItemTypeReference.IsODataEnumTypeKind() ||
expectedItemTypeReference.IsODataTypeDefinitionTypeKind(),
"If an expected type is specified, it must be a primitive, complex type or type definition.");
"If an expected type is specified, it must be a primitive, complex type, enum type or type definition.");
this.JsonReader.AssertNotBuffering();

object item = this.ReadNonEntityValue(
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OData.Core/ODataCollectionReaderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ public Scope(ODataCollectionReaderState state, object item, bool isCollectionEle
Debug.Assert(
state == ODataCollectionReaderState.Start && item == null ||
state == ODataCollectionReaderState.CollectionStart && item is ODataCollectionStart ||
state == ODataCollectionReaderState.Value && (item == null || item is ODataComplexValue || EdmLibraryExtensions.IsPrimitiveType(item.GetType())) ||
state == ODataCollectionReaderState.Value && (item == null || item is ODataComplexValue || EdmLibraryExtensions.IsPrimitiveType(item.GetType()) || item is ODataEnumValue) ||
state == ODataCollectionReaderState.CollectionEnd && item is ODataCollectionStart ||
state == ODataCollectionReaderState.Exception && item == null ||
state == ODataCollectionReaderState.Completed && item == null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
</Compile>
<Compile Include="Tests\Annotation\AnnotationTargetingOperationTestsProxy.cs" />
<Compile Include="Tests\Annotation\ClientAnnotationTests.cs" />
<Compile Include="Tests\Annotation\AnnotationRequestMessage.cs" />
<Compile Include="Tests\CustomizedHttpWebRequestMessage.cs" />
<Compile Include="Tests\Annotation\AnnotationTestsProxy.cs" />
<Compile Include="Tests\Annotation\ClientMetadataAnnotationTests.cs" />
<Compile Include="Tests\Annotation\ClientMetadataAnnotationWithPOCO.cs" />
Expand All @@ -83,6 +83,8 @@
<Compile Include="Tests\Materialization\ComplexValueMaterializationPolicyTests.cs" />
<Compile Include="Tests\Materialization\CollectionValueMaterializationPolicyTests.cs" />
<Compile Include="Tests\Materialization\EntryValueMaterializationPolicyUnitTests.cs" />
<Compile Include="Tests\Materialization\ODataMaterializerTests.cs" />
<Compile Include="Tests\Materialization\ODataMaterializerTestsProxy.cs" />
<Compile Include="Tests\Materialization\PrimitiveValueMaterializationPolicyTests.cs" />
<Compile Include="Tests\Materialization\PrimitivePropertyConverterTests.cs" />
<Compile Include="Tests\Materialization\TestEntityTracker.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,7 @@ private void SetResponse(string response)
{
dsc.Configurations.RequestPipeline.OnMessageCreating = (args) =>
{
return new AnnotationRequestMessage(args,
return new CustomizedHttpWebRequestMessage(args,
response,
new Dictionary<string, string>()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@
// </copyright>
//---------------------------------------------------------------------

namespace Microsoft.OData.Client.TDDUnitTests.Tests.Annotation
namespace Microsoft.OData.Client.TDDUnitTests.Tests
{
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.OData.Client;
using Microsoft.OData.Core;

public class AnnotationRequestMessage : HttpWebRequestMessage
public class CustomizedHttpWebRequestMessage : HttpWebRequestMessage
{
public string Response { get; set; }
public Dictionary<string, string> CutomizedHeaders { get; set; }

public AnnotationRequestMessage(DataServiceClientRequestMessageArgs args)
public CustomizedHttpWebRequestMessage(DataServiceClientRequestMessageArgs args)
: base(args)
{
}

public AnnotationRequestMessage(DataServiceClientRequestMessageArgs args, string response, Dictionary<string, string> headers)
public CustomizedHttpWebRequestMessage(DataServiceClientRequestMessageArgs args, string response, Dictionary<string, string> headers)
: base(args)
{
this.Response = response;
Expand Down
Loading

0 comments on commit 883f5a6

Please sign in to comment.