-
Notifications
You must be signed in to change notification settings - Fork 712
Support self referencing type substitution #382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7e87848
52cd033
566a133
bc6b151
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ public sealed class DefaultModelTypeBuilder : IModelTypeBuilder | |
readonly ICollection<Assembly> assemblies; | ||
readonly ConcurrentDictionary<ApiVersion, ModuleBuilder> modules = new ConcurrentDictionary<ApiVersion, ModuleBuilder>(); | ||
readonly ConcurrentDictionary<ClassSignature, Type> generatedTypes = new ConcurrentDictionary<ClassSignature, Type>(); | ||
private readonly Dictionary<string, Type> passedTypes = new Dictionary<string, Type>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In thinking about this, I'm pretty sure that using For example, imagine that There's two ways this can be handled:
Option 2 might look like: static int GetVisitedTypeKey( IEdmTypeReference type, ApiVersion apiVersion ) =>
( type.FullName().GetHashCode() * 397 ) ^ apiVersion.GetHashCode(); |
||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="DefaultModelTypeBuilder"/> 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 ) ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's capture the type name so we don't have to build it two times. Something like: var typeName = structuredTypeRef.FullName(); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This obviously supplanted by the struct key or hash code if you refactor |
||
{ | ||
properties.Add(new ClassProperty(property, passedType)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: the spacing is off by my rules, but this should be flagged by code analysis and auto-formatted by the .editorconfig settings. correcting this should be as easy as formatting the document.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It formats for methods, but I had to manually add the formatting rules for nameof, typeof etc. |
||
continue; | ||
} | ||
|
||
passedTypes.Add(structuredTypeRef.ToString(), propertyType); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding the visited type needs to be added toward the end; probably after line 102. The |
||
|
||
if ( structuredTypeRef.IsCollection() ) | ||
{ | ||
var collectionType = structuredTypeRef.AsCollection(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; } | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,6 +86,29 @@ public void type_should_match_edm_with_nested_entity_substitution() | |
innerType.Should().HaveProperty<string>( nameof( Contact.FirstName ) ); | ||
innerType.Should().HaveProperty<string>( nameof( Contact.LastName ) ); | ||
} | ||
|
||
[Fact] | ||
public void type_should_match_with_self_referencing_property_substitution() | ||
{ | ||
// arrange | ||
var modelBuilder = new ODataConventionModelBuilder(); | ||
var person = modelBuilder.EntitySet<Company>( "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<int>( nameof(Company.CompanyId) ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: spacing; format of the document should fix them all |
||
subsitutedType.Should().HaveProperty<string>( nameof(Company.Name) ); | ||
subsitutedType = subsitutedType.GetRuntimeProperties().Single( p => p.Name == nameof( Company.ParentCompany ) ).PropertyType; | ||
subsitutedType.GetRuntimeProperties().Should().HaveCount( 3 ); | ||
subsitutedType.Should().HaveProperty<int>( nameof(Company.CompanyId) ); | ||
subsitutedType.Should().HaveProperty<string>( nameof(Company.Name) ); | ||
} | ||
|
||
[Theory] | ||
[MemberData( nameof( SubstitutionData ) )] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file doesn't seem to have actually been changed. It appears that only the extra lines have been removed. It's not a huge deal, but there's not much sense in changing a file that doesn't need changing. |
||
|
||
<PropertyGroup> | ||
<TargetFramework>net452</TargetFramework> | ||
<RootNamespace>Microsoft</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Microsoft.AspNet.OData.Versioning.ApiExplorer\Microsoft.AspNet.OData.Versioning.ApiExplorer.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<Reference Include="System" /> | ||
<Reference Include="System.ComponentModel.DataAnnotations" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="System.Runtime" Version="4.1.0" /> | ||
<PackageReference Include="System.Threading.Tasks" Version="4.0.11" /> | ||
</ItemGroup> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net452</TargetFramework> | ||
<RootNamespace>Microsoft</RootNamespace> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Microsoft.AspNet.OData.Versioning.ApiExplorer\Microsoft.AspNet.OData.Versioning.ApiExplorer.csproj" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<Reference Include="System" /> | ||
<Reference Include="System.ComponentModel.DataAnnotations" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="System.Runtime" Version="4.1.0" /> | ||
<PackageReference Include="System.Threading.Tasks" Version="4.0.11" /> | ||
</ItemGroup> | ||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; } | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,6 +85,29 @@ public void type_should_match_edm_with_nested_entity_substitution() | |
innerType.Should().HaveProperty<int>( nameof( Contact.ContactId ) ); | ||
innerType.Should().HaveProperty<string>( nameof( Contact.FirstName ) ); | ||
innerType.Should().HaveProperty<string>( nameof( Contact.LastName ) ); | ||
} | ||
|
||
[Fact] | ||
public void type_should_match_with_self_referencing_property_substitution() | ||
{ | ||
// arrange | ||
var modelBuilder = new ODataConventionModelBuilder(); | ||
var person = modelBuilder.EntitySet<Company>( "Companies" ).EntityType; | ||
|
||
var context = NewContext( modelBuilder.GetEdmModel() ); | ||
var originalType = typeof(Company); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: spacing; format of the document should fix them all |
||
|
||
//act | ||
var subsitutedType = originalType.SubstituteIfNecessary( context ); | ||
|
||
// assert | ||
subsitutedType.GetRuntimeProperties().Should().HaveCount( 3 ); | ||
subsitutedType.Should().HaveProperty<int>( nameof(Company.CompanyId) ); | ||
subsitutedType.Should().HaveProperty<string>( nameof(Company.Name) ); | ||
subsitutedType = subsitutedType.GetRuntimeProperties().Single( p => p.Name == nameof( Company.ParentCompany ) ).PropertyType; | ||
subsitutedType.GetRuntimeProperties().Should().HaveCount( 3 ); | ||
subsitutedType.Should().HaveProperty<int>( nameof(Company.CompanyId) ); | ||
subsitutedType.Should().HaveProperty<string>( nameof(Company.Name) ); | ||
} | ||
|
||
[Theory] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let's use
visitedTypes
as it's more consistent with the pattern being used