Open ThomasBarnekow opened 6 years ago
Note sure it's NetCore related yet but certainly looks like a bug. There is code to handle this scenario in ODL (https://github.com/OData/odata.net/blob/75df8f44f2b81f984589790be4885b6ee8946ad0/src/Microsoft.OData.Core/UriParser/Parsers/KeyFinder.cs#L55-L73) and it appears you have it modeled correctly. I'll continue to investigate to understand why.
I can explain the metadata error: in the ASP.NET Core version of OData WebApi, the attribute routing convention is initialized at the first request. Because of the bug, that convention throws an exception and fails the request. If you comment out the problematic route attribute, i.e. the second one that has only the version key, the metadata request works as expected. I could change thius behaviour but I think it might make it harder to find faulty route attributes.
Not NetCore related per se, i.e. should repro on WebApi 6.x as well. The root of the issue with where the referential constraint for the model is defined: on the ProductVersions or Product navigation property. ODL is looking for it on ProductVersions where as this model, which matches our documentation, puts it on Products. Still investigating as to whether this should be fixed in the model & docs, WebApi or ODL. Stay tuned.
I've consulted with our OData committee member and we have decided that the ODL implementation for this scenario is incorrect. I filed an ODL issue here: https://github.com/OData/odata.net/issues/1071
I attempted to inject the constraint into the "ProductVersions" navigation property without success using this:
builder.EntityType<Product>()
.HasKey(p => p.ProductName)
.HasRequired(p => p.ProductVersions, (p, pv) => p.ProductName == pv.First().ProductName);
WebApi (correctly) fails to parse the expression "p.ProductName == pv.First().ProductName" because .First() is not a property of pv, which is of type ICollection
Since WebApi will not build the model that ODL will parse of this scenario, I can only think of two work-arounds, both involving XML and neither is clean: 1.) Use an XML chunk for the model. 2.) Use WebApi to construct the model, export to XML, insert the referential constraint, read the XML back into an EdmModel and return it.
The metadata generated by your model now is:
<EntityType Name="Product">
<Key>
<PropertyRef Name="ProductName"/>
</Key>
<Property Name="ProductName" MaxLength="100" Nullable="false" Type="Edm.String"/>
<NavigationProperty Name="ProductVersions" Type="Collection(OData_Test.ProductVersion)" ContainsTarget="true"/>
</EntityType>
For ODL to parse the path as you expect it to, the metadata would need to look like this:
<EntityType Name="Product">
<Key>
<PropertyRef Name="ProductName"/>
</Key>
<Property Name="ProductName" MaxLength="100" Nullable="false" Type="Edm.String"/>
<NavigationProperty Name="ProductVersions" Type="Collection(OData_Test.ProductVersion)" ContainsTarget="true">
<ReferentialConstraint ReferencedProperty="ProductName" Property="ProductName"/>
</NavigationProperty>
</EntityType>
This appears to be an issue in ODL. @robward-ms has opened OData/odata.net#1071 to track. We will wait to close this issue until the ODL issue is resolved and we can verify that it works correctly in WebAPI.
Section 4.3.3 (URLs for Related Entities with Referential Constraints) of OData Version 4.0. Part 2: URL Conventions Plus Errata 03 states that "if a navigation property leading to a related entity type has a partner navigation property that specifies a referential constraint, then those key properties of the related entity that take part in the referential constraint MAY be omitted from URLs". I am trying to support an OData route (see Reproduce steps) that is pretty similar to the one provided in the example. However, the corresponding controller method is not entered and when accessing the metadata endpoint, an exception is thrown (see Actual result).
Assemblies affected
Microsoft.AspNetCore.OData.dll (7.0.0-beta1)
Reproduce steps
Model (simplified)
DbContext (simplified)
Startup code (simplified)
Controller (simplified)
Expected result
Having successfully created
Product
andProductVersion
entities, e.g., "My Product" and "1.0.0", using Postman to POST to:https://localhost:44356/odata/Products('My Product')/ProductVersions('1.0.0')/Instances
should make my server return
201 Created
(and hit a breakpoint set in the above controller method). Further, I should also be able to GET the metadata athttps://localhost:44356/odata/$metadata
without errors.Actual result
When posting to the above endpoint using the "shortened key predicate of the related entity",
404 Not Found
is returned without hitting my breakpoint.Further, the following exception is thrown when getting the metadata:
Additional detail
When removing the shortened OData route
from the controller, no exception is thrown and I obviously still can't use that route. When posting to an endpoint based on the full route (which is not the preferred one as per OData)
everything works just fine.