-
Notifications
You must be signed in to change notification settings - Fork 357
Fix cast in expand #2300
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
Fix cast in expand #2300
Conversation
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.
Looks good to me, but I don't know enough of the inner working to ensure that the tests are sufficient.
Could you add some tests that verify that the sample queries you made actually return the expected results? And also tests to verify the validation (e.g. if the nav prop's type and the type segment's type are not related, the expected exception is thrown?
|
|
||
| ODataExpandPath pathToNavProp = new ODataExpandPath(pathSoFar); | ||
|
|
||
| IEdmTypeReference elementTypeReference = hasDerivedTypeSegment ? derivedType.ToTypeReference() : null; |
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.
Out of curiosity, why does the elementTypeReference default to null when there's no derived type segment as opposed to defaulting to the type of the property being expanded? #Resolved
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.
It's null since we can get it from the expanded property in these lines
odata.net/src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs
Lines 924 to 929 in 1452522
| BindingState state = new BindingState(config) | |
| { | |
| ImplicitRangeVariable = | |
| NodeFactory.CreateImplicitRangeVariable(elementType != null ? elementType : | |
| targetNavigationSource.EntityType().ToTypeReference(), targetNavigationSource) | |
| }; |
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.
I see that we are getting the type in SelectExpandBinder, but what is the contract with BindFilter, BindOrderby, etc? These private functions take an elementType which gets passed eventually to CreateBindingState where the logic is to fall back to the navigationSource, if it's not otherwise specified. But it's not clear in the various private methods that the elementType is nullable, and future code could make invalid assumptions about whether it may be null. If that private CreateBindingState method is only reached through these methods, would it be cleaner to compute the elementType from the targetNavigationSource here so it is never null in subsequent methods? Alternatively, maybe at least clarify in the description of the elementType parameter to the various methods (i.e., line 529) "The target element type if different from the type of the target navigation source." Logic is sound, I just want us to consider what is the cleaner/more intuitive contract for maintainability.
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.
@mikepizzo I have updated the description.
| if (implicitRangeVariableType != null && implicitRangeVariableType != this.edmType) | ||
| { | ||
| // Validate that the derived type and the base type are related. | ||
| UriEdmHelpers.CheckRelatedTo(this.state.ImplicitRangeVariable.TypeReference.Definition, this.edmType); |
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.
Does this check ensure that the relationship is one way? i.e. would it return false if the arguments were reversed:
UriEdmHelpers.CheckRelatedTo(this.edmType, this.state.ImplicitRangeVariable.TypeReference.Definition)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.
The order of the arguments doesn't matter
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.
Not a blocker, but is there a test that confirms that by any chance?
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.
I have refactored that logic. We don't need to run that validation here since it's validated in the previous level binding i.e
In this example
Orders?$expand=Customer/NS.VipCustomer($select=VipCustomerName)
We are binding the $select. However validation has already happened in the previous level $expand=Customer/NS.VipCustomer
| UriEdmHelpers.CheckRelatedTo(currentNavProp.ToEntityType(), derivedType); |
Test are here
Line 1802 in a510d92
| public void ExpandWithNavigationPropWithUndefinedTypeThrows(string query) |
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.
LGTM
| this.targetEdmType = lastSegment.TargetEdmType; | ||
|
|
||
| // If the last segment is a TypeSegment, we should assign the targetEdmType as lastSegment.EdmType | ||
| // instead of lastSegment.TargetEdmType which references the Base Type EdmType. |
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.
// instead of lastSegment.TargetEdmType which references the Base Type EdmType.
Shouldn't the TargetEdmType of a TypeSegment be the EdmType?
Do we have other places in code where we make the same assumption (that the targetEdmType of a Type Segment is the actual EdmType, not the base type? #Resolved
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.
Reverted this change since it was solving one use case $expand=Prop1($expand=Prop2).
Handling this in the GenerateExpandItem method in the SelectExpandBinder
|
Code looks good for addressing the issue, but please consider whether the targetTypeEdmType of a TypeSegment should be the actual type or the base type. Do we have other places in code that expects it to be the base type? Do we have other places in code that (erroneously) expects it to be the actual type? In reply to: 1030201271 |
Do we need similar logic here for taking a cast segment into account for $select? Anywhere else? In reply to: 1030205204 In reply to: 1030205204 Refers to: src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs:287 in 6a4c552. [](commit_id = 6a4c552, deletion_comment = False) |
src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs
Outdated
Show resolved
Hide resolved
Should we be passing elementTypeReference to BindCompute? In reply to: 1030205801 In reply to: 1030205801 Refers to: src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs:450 in 8d62b20. [](commit_id = 8d62b20, deletion_comment = False) |
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.
🕐
I have fixed this |
bde1e00 to
3dcfe53
Compare
3dcfe53 to
6aeaa3c
Compare
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.
The code looks good. I think adding some e2e tests that verify that the expected data is returned from a request would be great.
src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs
Outdated
Show resolved
Hide resolved
Is it as simple as changing this to lastSegment.EdmType? In reply to: 1030205204 Refers to: src/Microsoft.OData.Core/UriParser/Binders/SelectExpandBinder.cs:287 in 8d62b20. [](commit_id = 8d62b20, deletion_comment = False) |
|
This PR has Quantification details
Why proper sizing of changes matters
Optimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful? 👍 :ok_hand: :thumbsdown: (Email) |
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.
![]()
Issues
This pull request fixes OData/AspNetCoreOData#445
Description
In #2160 we added support for the
TypeSegmentafterNavigationPropertySegment.Example:
~/Orders?$expand=Customer/Model.VipCustomerThis means that we will expand only the Customers that are an instance of Model.VipCustomer.
Note:
Model.VipCustomeris a derived type of theCustomer.Something that was not captured in that PR is scenarios where we have query options affecting properties that are only in the derived type but not in the base type.
Examples:
~/Orders?$expand=Customer/Model.VipCustomer($filter=VipCustomerID eq '12345')~/Orders?$expand=Customer/Model.VipCustomer($expand=VipCustomerOrders')~/Orders?$expand=Customer/Model.VipCustomer($orderby=VipEmail)This PR fixes that by ensuring when we are doing semantic binding, we can bind the properties in the derived type.
Checklist (Uncheck if it is not completed)
Additional work necessary
If documentation update is needed, please add "Docs Needed" label to the issue and provide details about the required document change in the issue.