diff --git a/src/Common.OData.ApiExplorer/AspNet.OData/DefaultModelTypeBuilder.cs b/src/Common.OData.ApiExplorer/AspNet.OData/DefaultModelTypeBuilder.cs index a19e813f..f6a78460 100644 --- a/src/Common.OData.ApiExplorer/AspNet.OData/DefaultModelTypeBuilder.cs +++ b/src/Common.OData.ApiExplorer/AspNet.OData/DefaultModelTypeBuilder.cs @@ -30,6 +30,7 @@ public sealed class DefaultModelTypeBuilder : IModelTypeBuilder readonly ICollection assemblies; readonly ConcurrentDictionary modules = new ConcurrentDictionary(); readonly ConcurrentDictionary generatedTypes = new ConcurrentDictionary(); + private readonly Dictionary passedTypes = new Dictionary(); /// /// Initializes a new instance of the class. @@ -66,6 +67,14 @@ public Type NewStructuredType( IEdmStructuredType structuredType, Type clrType, var propertyType = property.PropertyType; var structuredTypeRef = structuralProperty.Type; + if ( passedTypes.TryGetValue( structuredTypeRef.ToString(), out var passedType ) ) + { + properties.Add(new ClassProperty(property, passedType)); + continue; + } + + passedTypes.Add(structuredTypeRef.ToString(), propertyType); + if ( structuredTypeRef.IsCollection() ) { var collectionType = structuredTypeRef.AsCollection(); diff --git a/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/AspNet.OData/Company.cs b/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/AspNet.OData/Company.cs new file mode 100644 index 00000000..fa4c99d3 --- /dev/null +++ b/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/AspNet.OData/Company.cs @@ -0,0 +1,12 @@ +namespace Microsoft.AspNet.OData +{ + public class Company + { + public int CompanyId { get; set; } + + public Company ParentCompany { get; set; } + + public string Name { get; set; } + + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/AspNet.OData/DefaultModelTypeBuilderTest.cs b/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/AspNet.OData/DefaultModelTypeBuilderTest.cs index 837bfd96..68f36648 100644 --- a/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/AspNet.OData/DefaultModelTypeBuilderTest.cs +++ b/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/AspNet.OData/DefaultModelTypeBuilderTest.cs @@ -86,6 +86,29 @@ public void type_should_match_edm_with_nested_entity_substitution() innerType.Should().HaveProperty( nameof( Contact.FirstName ) ); innerType.Should().HaveProperty( nameof( Contact.LastName ) ); } + + [Fact] + public void type_should_match_with_self_referencing_property_substitution() + { + // arrange + var modelBuilder = new ODataConventionModelBuilder(); + var person = modelBuilder.EntitySet( "Companies" ).EntityType; + + var context = NewContext( modelBuilder.GetEdmModel() ); + var originalType = typeof(Company); + + //act + var subsitutedType = originalType.SubstituteIfNecessary( context ); + + // assert + subsitutedType.GetRuntimeProperties().Should().HaveCount( 3 ); + subsitutedType.Should().HaveProperty( nameof(Company.CompanyId) ); + subsitutedType.Should().HaveProperty( nameof(Company.Name) ); + subsitutedType = subsitutedType.GetRuntimeProperties().Single( p => p.Name == nameof( Company.ParentCompany ) ).PropertyType; + subsitutedType.GetRuntimeProperties().Should().HaveCount( 3 ); + subsitutedType.Should().HaveProperty( nameof(Company.CompanyId) ); + subsitutedType.Should().HaveProperty( nameof(Company.Name) ); + } [Theory] [MemberData( nameof( SubstitutionData ) )] diff --git a/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests.csproj b/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests.csproj index 4df5f365..a1fdd62e 100644 --- a/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests.csproj +++ b/test/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests/Microsoft.AspNet.OData.Versioning.ApiExplorer.Tests.csproj @@ -1,23 +1,18 @@  - - - net452 - Microsoft - - - - - - - - - - - - - - - - - + + net452 + Microsoft + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Versioning.ApiExplorer.Tests/AspNet.OData/Company.cs b/test/Microsoft.AspNetCore.OData.Versioning.ApiExplorer.Tests/AspNet.OData/Company.cs new file mode 100644 index 00000000..fa4c99d3 --- /dev/null +++ b/test/Microsoft.AspNetCore.OData.Versioning.ApiExplorer.Tests/AspNet.OData/Company.cs @@ -0,0 +1,12 @@ +namespace Microsoft.AspNet.OData +{ + public class Company + { + public int CompanyId { get; set; } + + public Company ParentCompany { get; set; } + + public string Name { get; set; } + + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.OData.Versioning.ApiExplorer.Tests/AspNet.OData/DefaultModelTypeBuilderTest.cs b/test/Microsoft.AspNetCore.OData.Versioning.ApiExplorer.Tests/AspNet.OData/DefaultModelTypeBuilderTest.cs index 47c51d8e..d2e994da 100644 --- a/test/Microsoft.AspNetCore.OData.Versioning.ApiExplorer.Tests/AspNet.OData/DefaultModelTypeBuilderTest.cs +++ b/test/Microsoft.AspNetCore.OData.Versioning.ApiExplorer.Tests/AspNet.OData/DefaultModelTypeBuilderTest.cs @@ -85,6 +85,29 @@ public void type_should_match_edm_with_nested_entity_substitution() innerType.Should().HaveProperty( nameof( Contact.ContactId ) ); innerType.Should().HaveProperty( nameof( Contact.FirstName ) ); innerType.Should().HaveProperty( nameof( Contact.LastName ) ); + } + + [Fact] + public void type_should_match_with_self_referencing_property_substitution() + { + // arrange + var modelBuilder = new ODataConventionModelBuilder(); + var person = modelBuilder.EntitySet( "Companies" ).EntityType; + + var context = NewContext( modelBuilder.GetEdmModel() ); + var originalType = typeof(Company); + + //act + var subsitutedType = originalType.SubstituteIfNecessary( context ); + + // assert + subsitutedType.GetRuntimeProperties().Should().HaveCount( 3 ); + subsitutedType.Should().HaveProperty( nameof(Company.CompanyId) ); + subsitutedType.Should().HaveProperty( nameof(Company.Name) ); + subsitutedType = subsitutedType.GetRuntimeProperties().Single( p => p.Name == nameof( Company.ParentCompany ) ).PropertyType; + subsitutedType.GetRuntimeProperties().Should().HaveCount( 3 ); + subsitutedType.Should().HaveProperty( nameof(Company.CompanyId) ); + subsitutedType.Should().HaveProperty( nameof(Company.Name) ); } [Theory]