Skip to content

Commit

Permalink
entity navigation property support in complex types (#1743)
Browse files Browse the repository at this point in the history
* navigation property support in complex types

* Fixed based on review comments

Co-authored-by: elokerio <elokerio@ELOKERIO-TEMP>
  • Loading branch information
ElizabethOkerio and elokerio authored May 26, 2020
1 parent 9c2af9f commit 33e8a0b
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 41 deletions.
44 changes: 23 additions & 21 deletions src/Microsoft.OData.Client/ClientEdmModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -591,32 +591,34 @@ private IEdmProperty CreateEdmProperty(IEdmStructuredType declaringType, Propert
propertyEdmType.TypeKind == EdmTypeKind.Collection,
"Property kind should be Entity, Complex, Enum, Primitive or Collection.");

IEdmProperty edmProperty;
IEdmProperty edmProperty = null;
bool isPropertyNullable = ClientTypeUtil.CanAssignNull(propertyInfo.PropertyType);
if (propertyEdmType.TypeKind == EdmTypeKind.Entity || (propertyEdmType.TypeKind == EdmTypeKind.Collection && ((IEdmCollectionType)propertyEdmType).ElementType.TypeKind() == EdmTypeKind.Entity))
{
IEdmEntityType declaringEntityType = declaringType as IEdmEntityType;
if (declaringEntityType == null)
if (declaringType.TypeKind == EdmTypeKind.Entity || declaringType.TypeKind == EdmTypeKind.Complex)
{
throw c.Error.InvalidOperation(c.Strings.ClientTypeCache_NonEntityTypeCannotContainEntityProperties(propertyInfo.Name, propertyInfo.DeclaringType.ToString()));
}
if (declaringType as IEdmEntityType == null && declaringType as IEdmComplexType == null)
{
throw c.Error.InvalidOperation(c.Strings.ClientTypeCache_NonEntityTypeOrNonComplexTypeCannotContainEntityProperties(propertyInfo.Name, propertyInfo.DeclaringType.ToString()));
}

// Create a navigation property representing one side of an association.
// The partner representing the other side exists only inside this property and is not added to the target entity type,
// so it should not cause any name collisions.
edmProperty = EdmNavigationProperty.CreateNavigationPropertyWithPartner(
ClientTypeUtil.GetServerDefinedName(propertyInfo),
propertyEdmType.ToEdmTypeReference(isPropertyNullable),
/*dependentProperties*/ null,
/*principalProperties*/ null,
/*containsTarget*/ false,
EdmOnDeleteAction.None,
"Partner",
declaringEntityType.ToEdmTypeReference(true),
/*partnerDependentProperties*/ null,
/*partnerPrincipalProperties*/ null,
/*partnerContainsTarget*/ false,
EdmOnDeleteAction.None);
// Create a navigation property representing one side of an association.
// The partner representing the other side exists only inside this property and is not added to the target entity type,
// so it should not cause any name collisions.
edmProperty = EdmNavigationProperty.CreateNavigationPropertyWithPartner(
ClientTypeUtil.GetServerDefinedName(propertyInfo),
propertyEdmType.ToEdmTypeReference(isPropertyNullable),
/*dependentProperties*/ null,
/*principalProperties*/ null,
/*containsTarget*/ false,
EdmOnDeleteAction.None,
"Partner",
declaringType.ToEdmTypeReference(true),
/*partnerDependentProperties*/ null,
/*partnerPrincipalProperties*/ null,
/*partnerContainsTarget*/ false,
EdmOnDeleteAction.None);
}
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OData.Client/Microsoft.OData.Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ internal sealed class TextRes {
internal const string ClientType_MultipleTypesWithSameName = "ClientType_MultipleTypesWithSameName";
internal const string WebUtil_TypeMismatchInCollection = "WebUtil_TypeMismatchInCollection";
internal const string WebUtil_TypeMismatchInNonPropertyCollection = "WebUtil_TypeMismatchInNonPropertyCollection";
internal const string ClientTypeCache_NonEntityTypeCannotContainEntityProperties = "ClientTypeCache_NonEntityTypeCannotContainEntityProperties";
internal const string ClientTypeCache_NonEntityTypeOrNonComplexTypeCannotContainEntityProperties = "ClientTypeCache_NonEntityTypeOrNonComplexTypeCannotContainEntityProperties";
internal const string DataServiceException_GeneralError = "DataServiceException_GeneralError";
internal const string Deserialize_GetEnumerator = "Deserialize_GetEnumerator";
internal const string Deserialize_Current = "Deserialize_Current";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -736,10 +736,10 @@ internal static string WebUtil_TypeMismatchInNonPropertyCollection(object p0) {
}

/// <summary>
/// A string like "The property '{0}' is of entity type and it cannot be a property of the type '{1}', which is not of entity type. Only entity types can contain navigation properties."
/// A string like "The property '{0}' is of entity type and it cannot be a property of the type '{1}', which is not of entity type or complex type. Only entity types and complex types can contain navigation properties."
/// </summary>
internal static string ClientTypeCache_NonEntityTypeCannotContainEntityProperties(object p0, object p1) {
return Microsoft.OData.Client.TextRes.GetString(Microsoft.OData.Client.TextRes.ClientTypeCache_NonEntityTypeCannotContainEntityProperties, p0, p1);
internal static string ClientTypeCache_NonEntityTypeOrNonComplexTypeCannotContainEntityProperties(object p0, object p1) {
return Microsoft.OData.Client.TextRes.GetString(Microsoft.OData.Client.TextRes.ClientTypeCache_NonEntityTypeOrNonComplexTypeCannotContainEntityProperties, p0, p1);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OData.Edm/Microsoft.OData.Edm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal sealed class EdmRes {
internal const string EdmPath_UnexpectedKind = "EdmPath_UnexpectedKind";
internal const string Annotations_TypeMismatch = "Annotations_TypeMismatch";
internal const string Constructable_VocabularyAnnotationMustHaveTarget = "Constructable_VocabularyAnnotationMustHaveTarget";
internal const string Constructable_EntityTypeOrCollectionOfEntityTypeExpected = "Constructable_EntityTypeOrCollectionOfEntityTypeExpected";
internal const string Constructable_EntityTypeOrCollectionOfEntityTypeOrComplexTypeExpected = "Constructable_EntityTypeOrCollectionOfEntityTypeOrComplexTypeExpected";
internal const string Constructable_TargetMustBeStock = "Constructable_TargetMustBeStock";
internal const string TypeSemantics_CouldNotConvertTypeReference = "TypeSemantics_CouldNotConvertTypeReference";
internal const string EdmModel_CannotUseElementWithTypeNone = "EdmModel_CannotUseElementWithTypeNone";
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.OData.Edm/Parameterized.Microsoft.OData.Edm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ internal static string Constructable_VocabularyAnnotationMustHaveTarget {
}

/// <summary>
/// A string like "An entity type or a collection of an entity type is expected."
/// A string like "An entity type or a collection of an entity type or a complex type is expected."
/// </summary>
internal static string Constructable_EntityTypeOrCollectionOfEntityTypeExpected {
internal static string Constructable_EntityTypeOrCollectionOfEntityTypeOrComplexTypeExpected {
get {
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.Constructable_EntityTypeOrCollectionOfEntityTypeExpected);
return Microsoft.OData.Edm.EdmRes.GetString(Microsoft.OData.Edm.EdmRes.Constructable_EntityTypeOrCollectionOfEntityTypeOrComplexTypeExpected);
}
}

Expand Down
47 changes: 40 additions & 7 deletions src/Microsoft.OData.Edm/Schema/EdmNavigationProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,17 +181,32 @@ public static EdmNavigationProperty CreateNavigationPropertyWithPartner(
EdmUtil.CheckArgumentNull(propertyType, "propertyType");
EdmUtil.CheckArgumentNull(partnerPropertyName, "partnerPropertyName");
EdmUtil.CheckArgumentNull(partnerPropertyType, "partnerPropertyType");

IEdmEntityType declaringType = GetEntityType(partnerPropertyType);
if (declaringType == null)
IEdmStructuredType declaringType = null;
if (partnerPropertyType.Definition.TypeKind == EdmTypeKind.Entity)
{
declaringType = GetEntityType(partnerPropertyType) as IEdmEntityType;
if (declaringType == null)
{
throw new ArgumentException(Strings.Constructable_EntityTypeOrCollectionOfEntityTypeOrComplexTypeExpected, "partnerPropertyType");
}
}
else if (partnerPropertyType.Definition.TypeKind == EdmTypeKind.Complex)
{
throw new ArgumentException(Strings.Constructable_EntityTypeOrCollectionOfEntityTypeExpected, "partnerPropertyType");
declaringType = GetComplexType(partnerPropertyType) as IEdmComplexType;
if (declaringType == null)
{
throw new ArgumentException(Strings.Constructable_EntityTypeOrCollectionOfEntityTypeOrComplexTypeExpected, "partnerPropertyType");
}
}
else
{
throw new ArgumentException(Strings.Constructable_EntityTypeOrCollectionOfEntityTypeOrComplexTypeExpected, "partnerPropertyType");
}

IEdmEntityType partnerDeclaringType = GetEntityType(propertyType);
if (partnerDeclaringType == null)
{
throw new ArgumentException(Strings.Constructable_EntityTypeOrCollectionOfEntityTypeExpected, "propertyType");
throw new ArgumentException(Strings.Constructable_EntityTypeOrCollectionOfEntityTypeOrComplexTypeExpected, "propertyType");
}

EdmNavigationProperty end1 = new EdmNavigationProperty(
Expand Down Expand Up @@ -225,8 +240,8 @@ public static EdmNavigationProperty CreateNavigationPropertyWithPartner(
internal void SetPartner(IEdmNavigationProperty navigationProperty, IEdmPathExpression navigationPropertyPath)
{
Debug.Assert(
DeclaringType is IEdmEntityType,
"Partner info cannot be set for nav. property on a non-entity type.");
DeclaringType is IEdmEntityType || DeclaringType is IEdmComplexType,
"Partner info cannot be set for nav. property on a non-entity or non-complex type.");
partner = navigationProperty;
PartnerPath = navigationPropertyPath;
}
Expand All @@ -249,6 +264,24 @@ private static IEdmEntityType GetEntityType(IEdmTypeReference type)

return entityType;
}
private static IEdmComplexType GetComplexType(IEdmTypeReference type)
{
if (type.IsComplex())
{
return (IEdmComplexType)type.Definition;

}
else if (type.IsCollection())
{
type = ((IEdmCollectionType)type.Definition).ElementType;
if (type.IsComplex())
{
return (IEdmComplexType)type.Definition;
}
}

return null;
}

private static IEdmTypeReference CreateNavigationPropertyType(IEdmEntityType entityType, EdmMultiplicity multiplicity, string multiplicityParameterName)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//---------------------------------------------------------------------
// <copyright file="ComplexNavigationTests.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
//---------------------------------------------------------------------

using Microsoft.OData.Client.Metadata;
using Microsoft.OData.Edm;
using System;
using System.Collections.Generic;
using Xunit;

namespace Microsoft.OData.Client.Tests.ComplexNavigations
{
/// <summary>
///tests to show that it is possible to have an entity navigation on a complex type.
/// </summary>
public class ComplexNavigationTests
{
/// <summary>
/// This test checks whether the declaring type kind of the navigation property created is complex.
/// </summary>
[Fact]
public void DeclaringTypeOFAnEntityNavigationCanBeAComplexType()
{
//Arrange
Type complexDeclaringType = typeof(Address);
Type entityNavigationType = typeof(City);
EdmTypeKind expectedDeclaringTypeKind = EdmTypeKind.Complex;

//Act
ClientEdmModel clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V401);
IEdmType edmTypeOfComplexDeclaringType = clientEdmModel.GetOrCreateEdmType(complexDeclaringType);
IEdmType edmTypeOfEntityNavigationType = clientEdmModel.GetOrCreateEdmType(entityNavigationType);
IEdmStructuredType entiyNavigationType = clientEdmModel.GetOrCreateEdmType(complexDeclaringType) as IEdmStructuredType;
EdmNavigationProperty edmNavigationProperty = EdmNavigationProperty.CreateNavigationPropertyWithPartner("City",ClientTypeUtil.ToEdmTypeReference(edmTypeOfEntityNavigationType, true),null,null,false, EdmOnDeleteAction.None, "Partner", ClientTypeUtil.ToEdmTypeReference(edmTypeOfComplexDeclaringType, true),null,null,false,EdmOnDeleteAction.None);
EdmTypeKind resultingDeclaringTypeKind = edmNavigationProperty.DeclaringType.TypeKind;

//Assert
Assert.Equal(expectedDeclaringTypeKind, resultingDeclaringTypeKind);
}
}

[EntityType]
[Key("UserName")]
public class Person
{
public string UserName { get; set; }
public Address Address { get; set; }
public List<Address> Addresses { get; set; }
}

[EntityType]
[Key("Name")]
public class City
{
public string Name { get; set; }
}

public class Address
{
public string Road { get; set; }
public City City { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1568,11 +1568,6 @@ public void ClientTypeCacheError_LoadProperties()
{
ex = ex.InnerException;
}

Assert.AreEqual(DataServicesClientResourceUtil.GetString(
"ClientTypeCache_NonEntityTypeCannotContainEntityProperties",
"NavProp",
"AstoriaUnitTests.Tests.ClientCSharpRegressionTests+ClientTypeCacheError_NonEntityType"), ex.Message);
}
}

Expand Down

0 comments on commit 33e8a0b

Please sign in to comment.