Skip to content

Commit a9d851a

Browse files
authored
fixes #2835: omit the ODataIdContainer from model builder, change the serializer (#2836)
* fixes #2835: omit the ODataIdContainer from model builder, change the serializer * remove unused test * Address the comments * fix the null reference
1 parent 7dbaf97 commit a9d851a

6 files changed

Lines changed: 90 additions & 34 deletions

File tree

src/Microsoft.AspNet.OData.Shared/Builder/ODataConventionModelBuilder.cs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,11 @@ private void MapStructuralType(StructuralTypeConfiguration structuralType)
551551
bool isCollection;
552552
IEdmTypeConfiguration mappedType;
553553

554-
PropertyKind propertyKind = GetPropertyType(property, out isCollection, out mappedType);
554+
PropertyKind propertyKind = GetPropertyType(property, out isCollection, out mappedType, out bool mapped);
555+
if (!mapped)
556+
{
557+
continue;
558+
}
555559

556560
if (propertyKind == PropertyKind.Primitive || propertyKind == PropertyKind.Complex || propertyKind == PropertyKind.Enum)
557561
{
@@ -694,41 +698,52 @@ private void MapStructuralProperty(StructuralTypeConfiguration type, PropertyInf
694698

695699
// figures out the type of the property (primitive, complex, navigation) and the corresponding edm type if we have seen this type
696700
// earlier or the user told us about it.
697-
private PropertyKind GetPropertyType(PropertyInfo property, out bool isCollection, out IEdmTypeConfiguration mappedType)
701+
private PropertyKind GetPropertyType(PropertyInfo property, out bool isCollection, out IEdmTypeConfiguration mappedType, out bool mapped)
698702
{
699703
Contract.Assert(property != null);
700704

705+
mapped = true;
706+
mappedType = null;
707+
isCollection = false;
708+
709+
if (typeof(ODataIdContainer).IsAssignableFrom(property.PropertyType))
710+
{
711+
mapped = false;
712+
return (PropertyKind)(int.MaxValue);
713+
}
714+
701715
// IDictionary<string, object> is used as a container to save/retrieve dynamic properties for an open type.
702716
// It is different from other collections (for example, IEnumerable<T> or IDictionary<string, int>)
703717
// which are used as navigation properties.
704718
if (typeof(IDictionary<string, object>).IsAssignableFrom(property.PropertyType))
705719
{
706-
mappedType = null;
707-
isCollection = false;
708720
return PropertyKind.Dynamic;
709721
}
710722

711723
// IODataInstanceAnnotationContainer is used as a container to save/retrieve instance annotation properties for a CLR type.
712724
// It is different from other collections (for example, IDictionary<string,IDictionary<string, int>>)
713725
if (typeof(IODataInstanceAnnotationContainer).IsAssignableFrom(property.PropertyType))
714726
{
715-
mappedType = null;
716-
isCollection = false;
717-
718727
return PropertyKind.InstanceAnnotations;
719728
}
720729

721730
PropertyKind propertyKind;
722731
if (TryGetPropertyTypeKind(property.PropertyType, out mappedType, out propertyKind))
723732
{
724-
isCollection = false;
725733
return propertyKind;
726734
}
727735

728736
Type elementType;
729737
if (TypeHelper.IsCollection(property.PropertyType, out elementType))
730738
{
731739
isCollection = true;
740+
741+
if (typeof(ODataIdContainer).IsAssignableFrom(elementType))
742+
{
743+
mapped = false;
744+
return (PropertyKind)(int.MaxValue);
745+
}
746+
732747
if (TryGetPropertyTypeKind(elementType, out mappedType, out propertyKind))
733748
{
734749
return propertyKind;

src/Microsoft.AspNet.OData.Shared/DeltaSetOfT.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public DeltaSet<TStructuralType> Patch(ICollection<TStructuralType> originalColl
6666
}
6767

6868
/// <summary>
69-
/// Asnchronously patch a DeltaSet, a collection for Delta<typeparamref name="TStructuralType"/>.
69+
/// Asynchronously patch a DeltaSet, a collection for Delta<typeparamref name="TStructuralType"/>.
7070
/// </summary>
7171
/// <param name="originalCollection">Original collection of the type which needs to be updated.</param>
7272
/// <returns>A task representing a DeltaSet response.</returns>
@@ -104,7 +104,7 @@ public async Task<DeltaSet<TStructuralType>> PatchAsync(ODataAPIHandler<TStructu
104104
/// <param name="apiHandlerFactory">API Handler Factory.</param>
105105
/// <returns>DeltaSet response.</returns>
106106
public DeltaSet<TStructuralType> Patch(ODataAPIHandler<TStructuralType> apiHandlerOfT, ODataAPIHandlerFactory apiHandlerFactory)
107-
{
107+
{
108108
Debug.Assert(apiHandlerOfT != null, "apiHandlerOfT != null");
109109

110110
return CopyChangedValues(apiHandlerOfT, apiHandlerFactory);

src/Microsoft.AspNet.OData.Shared/Formatter/Deserialization/ODataResourceDeserializerHelpers.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ internal static void ApplyODataIdContainer(object resource, ODataResourceWrapper
257257
if (path.EdmType.AsElementType() is IEdmEntityType entityType)
258258
{
259259
//Setting Odataid , for POCO classes, as a property in the POCO object itself(if user has OdataIDContainer property),
260-
//for Delta and EdmEntity object setting as an added property ODataIdcontianer in those classes
260+
//for Delta and EdmEntity object setting as an added property ODataIdContainer in those classes
261261
ODataPath odataPath = new ODataPath(path.Segments);
262262

263263
// if there is no Id on the resource, try to compute one from path
@@ -286,6 +286,7 @@ internal static void ApplyODataIdContainer(object resource, ODataResourceWrapper
286286
}
287287
else
288288
{
289+
// TODO: the logic to use the first 'ODataIdContainer' property as the container looks simple and error
289290
PropertyInfo containerPropertyInfo = EdmLibHelpers.GetClrType(entityType, readContext.Model).GetProperties().FirstOrDefault(x => x.PropertyType == typeof(ODataIdContainer));
290291
if (containerPropertyInfo != null)
291292
{

src/Microsoft.AspNet.OData.Shared/Formatter/EdmLibHelpers.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,17 +291,20 @@ public static Type GetClrType(IEdmType edmType, IEdmModel edmModel, IWebApiAssem
291291
}
292292

293293
string typeName = edmSchemaType.FullName();
294-
IEnumerable<Type> matchingTypes = GetMatchingTypes(typeName, assembliesResolver);
295294

296-
if (matchingTypes.Count() > 1)
295+
// Calling "ToList()" to avoid multiple enumerates.
296+
IList<Type> matchingTypes = GetMatchingTypes(typeName, assembliesResolver).ToList();
297+
298+
if (matchingTypes.Count > 1)
297299
{
298300
throw Error.Argument("edmTypeReference", SRResources.MultipleMatchingClrTypesForEdmType,
299-
typeName, String.Join(",", matchingTypes.Select(type => type.AssemblyQualifiedName)));
301+
typeName, String.Join(",", matchingTypes.Select(t => t.AssemblyQualifiedName)));
300302
}
301303

302-
edmModel.SetAnnotationValue<ClrTypeAnnotation>(edmSchemaType, new ClrTypeAnnotation(matchingTypes.SingleOrDefault()));
304+
Type clrType = matchingTypes.Count == 0 ? null : matchingTypes[0];
305+
edmModel.SetAnnotationValue(edmSchemaType, new ClrTypeAnnotation(clrType));
303306

304-
return matchingTypes.SingleOrDefault();
307+
return clrType;
305308
}
306309

307310
public static bool IsNotFilterable(IEdmProperty edmProperty, IEdmProperty pathEdmProperty,

src/Microsoft.AspNet.OData.Shared/Formatter/Serialization/ODataResourceSerializer.cs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,11 @@ internal void WriteDeltaComplexProperties(SelectExpandNode selectExpandNode,
697697
Contract.Assert(resourceContext != null);
698698
Contract.Assert(writer != null);
699699

700-
IEnumerable<KeyValuePair<IEdmStructuralProperty, PathSelectItem>> complexProperties = GetPropertiesToWrite(selectExpandNode, resourceContext);
700+
IDictionary<IEdmStructuralProperty, PathSelectItem> complexProperties = GetPropertiesToWrite(selectExpandNode, resourceContext);
701+
if (complexProperties == null)
702+
{
703+
return;
704+
}
701705

702706
foreach (KeyValuePair<IEdmStructuralProperty, PathSelectItem> complexProperty in complexProperties)
703707
{
@@ -781,7 +785,11 @@ internal async Task WriteDeltaComplexPropertiesAsync(SelectExpandNode selectExpa
781785
Contract.Assert(resourceContext != null);
782786
Contract.Assert(writer != null);
783787

784-
IEnumerable<KeyValuePair<IEdmStructuralProperty, PathSelectItem>> complexProperties = GetPropertiesToWrite(selectExpandNode, resourceContext);
788+
IDictionary<IEdmStructuralProperty, PathSelectItem> complexProperties = GetPropertiesToWrite(selectExpandNode, resourceContext);
789+
if (complexProperties == null)
790+
{
791+
return;
792+
}
785793

786794
foreach (KeyValuePair<IEdmStructuralProperty, PathSelectItem> complexProperty in complexProperties)
787795
{
@@ -1643,7 +1651,11 @@ private void WriteComplexProperties(SelectExpandNode selectExpandNode, ResourceC
16431651
Contract.Assert(resourceContext != null);
16441652
Contract.Assert(writer != null);
16451653

1646-
IEnumerable<KeyValuePair<IEdmStructuralProperty, PathSelectItem>> complexProperties = GetPropertiesToWrite(selectExpandNode, resourceContext);
1654+
IDictionary<IEdmStructuralProperty, PathSelectItem> complexProperties = GetPropertiesToWrite(selectExpandNode, resourceContext);
1655+
if (complexProperties == null)
1656+
{
1657+
return;
1658+
}
16471659

16481660
foreach (KeyValuePair<IEdmStructuralProperty, PathSelectItem> selectedComplex in complexProperties)
16491661
{
@@ -1666,7 +1678,11 @@ private async Task WriteComplexPropertiesAsync(SelectExpandNode selectExpandNode
16661678
Contract.Assert(resourceContext != null);
16671679
Contract.Assert(writer != null);
16681680

1669-
IEnumerable<KeyValuePair<IEdmStructuralProperty, PathSelectItem>> complexProperties = GetPropertiesToWrite(selectExpandNode, resourceContext);
1681+
IDictionary<IEdmStructuralProperty, PathSelectItem> complexProperties = GetPropertiesToWrite(selectExpandNode, resourceContext);
1682+
if (complexProperties == null)
1683+
{
1684+
return;
1685+
}
16701686

16711687
foreach (KeyValuePair<IEdmStructuralProperty, PathSelectItem> selectedComplex in complexProperties)
16721688
{
@@ -1734,7 +1750,7 @@ private async Task WriteStreamPropertiesAsync(SelectExpandNode selectExpandNode,
17341750
}
17351751
}
17361752

1737-
private IEnumerable<KeyValuePair<IEdmStructuralProperty, PathSelectItem>> GetPropertiesToWrite(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
1753+
private static IDictionary<IEdmStructuralProperty, PathSelectItem> GetPropertiesToWrite(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
17381754
{
17391755
IDictionary<IEdmStructuralProperty, PathSelectItem> complexProperties = selectExpandNode.SelectedComplexTypeProperties;
17401756

@@ -1748,26 +1764,23 @@ private IEnumerable<KeyValuePair<IEdmStructuralProperty, PathSelectItem>> GetPro
17481764
changedProperties = deltaObject.GetChangedPropertyNames();
17491765
}
17501766

1751-
foreach (KeyValuePair<IEdmStructuralProperty, PathSelectItem> complexProperty in complexProperties)
1767+
if (changedProperties != null)
17521768
{
1753-
if (changedProperties == null || changedProperties.Contains(complexProperty.Key.Name))
1769+
IDictionary<IEdmStructuralProperty, PathSelectItem> propertiesToReturn
1770+
= new Dictionary<IEdmStructuralProperty, PathSelectItem>();
1771+
foreach (KeyValuePair<IEdmStructuralProperty, PathSelectItem> complexProperty in complexProperties)
17541772
{
1755-
IEdmTypeReference type = complexProperty.Key?.Type;
1756-
1757-
if (type != null && type.IsStructured() && resourceContext.EdmModel != null)
1773+
if (changedProperties.Contains(complexProperty.Key.Name))
17581774
{
1759-
Type clrType = EdmLibHelpers.GetClrType(type.AsStructured(), resourceContext.EdmModel);
1760-
1761-
if (clrType != null && clrType == typeof(ODataIdContainer))
1762-
{
1763-
continue;
1764-
}
1775+
propertiesToReturn[complexProperty.Key] = complexProperty.Value;
17651776
}
1766-
1767-
yield return complexProperty;
17681777
}
1778+
1779+
return propertiesToReturn;
17691780
}
17701781
}
1782+
1783+
return complexProperties;
17711784
}
17721785

17731786
private IEnumerable<KeyValuePair<IEdmNavigationProperty, Type>> GetNavigationPropertiesToWrite(SelectExpandNode selectExpandNode, ResourceContext resourceContext)

test/UnitTest/Microsoft.AspNet.OData.Test.Shared/Builder/Conventions/ODataConventionModelBuilderTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2374,6 +2374,30 @@ public void CanBuildModelForAnonymousTypes()
23742374
entity.AssertHasNavigationProperty(model, "NavigationCollection", new { ID = default(int) }.GetType(), isNullable: false, multiplicity: EdmMultiplicity.Many);
23752375
}
23762376

2377+
public class TypeContainsODataIdContainer
2378+
{
2379+
public int Id { get; set; }
2380+
public string Name { get; set; }
2381+
public ODataIdContainer Container { get; set; }
2382+
public IList<ODataIdContainer> Containers { get; set; }
2383+
}
2384+
2385+
[Fact]
2386+
public void CanBuildModel_OmitODataIdContainerProperties()
2387+
{
2388+
var configuration = RoutingConfigurationFactory.Create();
2389+
ODataConventionModelBuilder builder = ODataConventionModelBuilderFactory.Create(configuration, isQueryCompositionMode: true);
2390+
builder.EntityType<TypeContainsODataIdContainer>();
2391+
2392+
IEdmModel model = builder.GetEdmModel();
2393+
2394+
IEdmEntityType entity = model.AssertHasEntityType(typeof(TypeContainsODataIdContainer));
2395+
2396+
Assert.Equal(2, entity.Properties().Count());
2397+
entity.AssertHasKey(model, "Id", EdmPrimitiveTypeKind.Int32);
2398+
entity.AssertHasPrimitiveProperty(model, "Name", EdmPrimitiveTypeKind.String, true);
2399+
}
2400+
23772401
[Theory]
23782402
[InlineData(typeof(object[]))]
23792403
[InlineData(typeof(IEnumerable<object>))]

0 commit comments

Comments
 (0)