diff --git a/src/Microsoft.OData.Client/ClientEdmModel.cs b/src/Microsoft.OData.Client/ClientEdmModel.cs
index 4c0a12ffbd..bef3e3a9c5 100644
--- a/src/Microsoft.OData.Client/ClientEdmModel.cs
+++ b/src/Microsoft.OData.Client/ClientEdmModel.cs
@@ -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
{
diff --git a/src/Microsoft.OData.Client/Microsoft.OData.Client.cs b/src/Microsoft.OData.Client/Microsoft.OData.Client.cs
index ca11db7e84..d0ef244943 100644
--- a/src/Microsoft.OData.Client/Microsoft.OData.Client.cs
+++ b/src/Microsoft.OData.Client/Microsoft.OData.Client.cs
@@ -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";
diff --git a/src/Microsoft.OData.Client/Parameterized.Microsoft.OData.Client.cs b/src/Microsoft.OData.Client/Parameterized.Microsoft.OData.Client.cs
index dfca345056..cb12747d51 100644
--- a/src/Microsoft.OData.Client/Parameterized.Microsoft.OData.Client.cs
+++ b/src/Microsoft.OData.Client/Parameterized.Microsoft.OData.Client.cs
@@ -736,10 +736,10 @@ internal static string WebUtil_TypeMismatchInNonPropertyCollection(object p0) {
}
///
- /// 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."
///
- 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);
}
///
diff --git a/src/Microsoft.OData.Edm/Microsoft.OData.Edm.cs b/src/Microsoft.OData.Edm/Microsoft.OData.Edm.cs
index 70523f3660..6200c3a4e9 100644
--- a/src/Microsoft.OData.Edm/Microsoft.OData.Edm.cs
+++ b/src/Microsoft.OData.Edm/Microsoft.OData.Edm.cs
@@ -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";
diff --git a/src/Microsoft.OData.Edm/Parameterized.Microsoft.OData.Edm.cs b/src/Microsoft.OData.Edm/Parameterized.Microsoft.OData.Edm.cs
index 2df1e90dab..111ad26c66 100644
--- a/src/Microsoft.OData.Edm/Parameterized.Microsoft.OData.Edm.cs
+++ b/src/Microsoft.OData.Edm/Parameterized.Microsoft.OData.Edm.cs
@@ -50,11 +50,11 @@ internal static string Constructable_VocabularyAnnotationMustHaveTarget {
}
///
- /// 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."
///
- 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);
}
}
diff --git a/src/Microsoft.OData.Edm/Schema/EdmNavigationProperty.cs b/src/Microsoft.OData.Edm/Schema/EdmNavigationProperty.cs
index 1c22c644ec..d71570a0a3 100644
--- a/src/Microsoft.OData.Edm/Schema/EdmNavigationProperty.cs
+++ b/src/Microsoft.OData.Edm/Schema/EdmNavigationProperty.cs
@@ -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(
@@ -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;
}
@@ -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)
{
diff --git a/test/FunctionalTests/Microsoft.OData.Client.Tests/ComplexNavigations/ComplexNavigationTests.cs b/test/FunctionalTests/Microsoft.OData.Client.Tests/ComplexNavigations/ComplexNavigationTests.cs
new file mode 100644
index 0000000000..7943f3feea
--- /dev/null
+++ b/test/FunctionalTests/Microsoft.OData.Client.Tests/ComplexNavigations/ComplexNavigationTests.cs
@@ -0,0 +1,65 @@
+//---------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
+//
+//---------------------------------------------------------------------
+
+using Microsoft.OData.Client.Metadata;
+using Microsoft.OData.Edm;
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace Microsoft.OData.Client.Tests.ComplexNavigations
+{
+ ///
+ ///tests to show that it is possible to have an entity navigation on a complex type.
+ ///
+ public class ComplexNavigationTests
+ {
+ ///
+ /// This test checks whether the declaring type kind of the navigation property created is complex.
+ ///
+ [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 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; }
+ }
+}
diff --git a/test/FunctionalTests/Tests/DataServices/UnitTests/ClientCSharpUnitTests/DataWebClientCSharp/ClientCSharpRegressionTests.cs b/test/FunctionalTests/Tests/DataServices/UnitTests/ClientCSharpUnitTests/DataWebClientCSharp/ClientCSharpRegressionTests.cs
index 2802bf2553..696dadab4d 100644
--- a/test/FunctionalTests/Tests/DataServices/UnitTests/ClientCSharpUnitTests/DataWebClientCSharp/ClientCSharpRegressionTests.cs
+++ b/test/FunctionalTests/Tests/DataServices/UnitTests/ClientCSharpUnitTests/DataWebClientCSharp/ClientCSharpRegressionTests.cs
@@ -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);
}
}