Open ankitpatel5989 opened 4 years ago
@ankitpatel5989 What version of WebApi OData stack and EF/EF Core are you using? Also can you share the metadata?
@ankitpatel5989 Would you please share us a repro to dig more?
At my side, i tried to create the model based on your C# model.
Below is the metadata:
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="AspNetCore3xEndpointSample.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Product">
<Key>
<PropertyRef Name="ProductId" />
</Key>
<Property Name="ProductId" Type="Edm.Int32" Nullable="false" />
<Property Name="ProductName" Type="Edm.String" />
<Property Name="Depid" Type="Edm.String" />
<NavigationProperty Name="Department" Type="AspNetCore3xEndpointSample.Models.Department" />
</EntityType>
<EntityType Name="Department">
<Key>
<PropertyRef Name="Depid" />
</Key>
<Property Name="Depid" Type="Edm.String" Nullable="false" />
<Property Name="DepName" Type="Edm.String" />
</EntityType>
</Schema>
<Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="Container">
<EntitySet Name="Products" EntityType="AspNetCore3xEndpointSample.Models.Product">
<NavigationPropertyBinding Path="Department" Target="Departments" />
</EntitySet>
<EntitySet Name="Departments" EntityType="AspNetCore3xEndpointSample.Models.Department" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
My products table:
My department table:
The query works fine at my side: http://localhost:5000/odata/Products?$expand=Department
{
"@odata.context": "http://localhost:5000/odata/$metadata#Products(Department())",
"value": [
{
"ProductId": 1,
"ProductName": "Alexander",
"Depid": "1",
"Department": {
"Depid": "1",
"DepName": "Chemistry"
}
},
{
"ProductId": 2,
"ProductName": "Alonso",
"Depid": "1",
"Department": {
"Depid": "1",
"DepName": "Chemistry"
}
},
{
"ProductId": 3,
"ProductName": "Anand",
"Depid": "2",
"Department": {
"Depid": "2",
"DepName": "Microeconomics"
}
},
{
"ProductId": 4,
"ProductName": "abcdae",
"Depid": "133",
"Department": null
},
{
"ProductId": 5,
"ProductName": "455",
"Depid": "1",
"Department": {
"Depid": "1",
"DepName": "Chemistry"
}
}
]
}
Thank you very much for investigating the issue.
Few findings from my side: Sorry but I can't share my actual model with you. One thing I observed we have below properties with mentioned datatype exist. public byte? AllowSubDept { get; set; } //(DBType:tinyint, null) public string DepName { get; set; } //(DBType: varchar(80), null) public DateTime? AddedDate { get; set; } //(DBType: smalldatetime, null) public short? SecType { get; set; } //(DBType:smallint, null)
Can you please retry after adding those in Department table?
We also get below internal error which we debug it in our visual studio: An unhandled exception occurred while processing the request. InvalidOperationException: The EDM instance of type '[Domain.Models.Department Nullable=True]' is missing the property 'depid'.
I missed one more change in Department entitytype. In my metadata depid is nullable. In your investigation its not nullable (Property Name="Depid" Type="Edm.String" Nullable="false").
What version of WebApi OData stack and EF/EF Core are you using? Odata v4 and EF Core (2.2.6) Also can you share the metadata? Sorry but I cant share actual one. But i mentioned changes here which not matched with my metadata.
Do you have any update?
Facing similar issue in other page as well. Here its not giving error but getting same record multiple time instead of getting all records in $expand.
Can you please share code where you defined relationship?
So I had a very similar issue, and noticed that the table that I was expanding to had a EFCore Query Filter on it.
When the parent record had an {childtable}-id
that was filtered out of the expanded table (via the Query Filter), the record was not returned.
So OData did not return records that contained an ID that was filtered out via the QueryFilter.
But what also happens is that the $top and $count parameters don't honor that filter. So it would return a count of 14, while only returning 4 values in the value
property.
After updating from 7.3.0 to 7.5.2 we are experiencing a similar issue which I suppose to have the same cause. In our case this affects nullable complex properties.
Metadata (abbreviated for clarity):
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="AspNetSample.TypeDetails" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<ComplexType Name="SAContractDueTypeDetail">
<Property Name="Description" Type="Edm.String"/>
<Property Name="Value" Type="Edm.Int64" Nullable="false"/>
<Property Name="Abbrev" Type="Edm.String"/>
<Property Name="Text" Type="Edm.String"/>
</ComplexType>
</Schema>
<Schema Namespace="AspNetSample.ServiceOrder" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="SAContract">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Due" Type="AspNetSample.TypeDetails.SAContractDueTypeDetail"/>
<Property Name="Id" Type="Edm.Int64" Nullable="false"/>
</EntityType>
</Schema>
<Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="Container">
<EntitySet Name="SAContracts" EntityType="AspNetSample.ServiceOrder.SAContract"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
The property Due
having a null value is a common use case. Where this would correctly result in {"Id":15,"Due":null}
with 7.3.0, it now throws
System.InvalidOperationException: The EDM instance of type '[AspNetSample.TypeDetails.SAContractDueTypeDetail Nullable=True]' is missing the property 'Description'.
at Microsoft.AspNet.OData.ResourceContext.GetPropertyValue(System.String propertyName) at offset 73
at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, Microsoft.AspNet.OData.ResourceContext resourceContext) at offset 92
at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.CreateStructuralPropertyBag(Microsoft.AspNet.OData.Formatter.Serialization.SelectExpandNode selectExpandNode, Microsoft.AspNet.OData.ResourceContext resourceContext) at offset 113
at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.CreateResource(Microsoft.AspNet.OData.Formatter.Serialization.SelectExpandNode selectExpandNode, Microsoft.AspNet.OData.ResourceContext resourceContext) at offset 84
at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteResource(System.Object graph, Microsoft.OData.ODataWriter writer, Microsoft.AspNet.OData.Formatter.Serialization.ODataSerializerContext writeContext, Microsoft.OData.Edm.IEdmTypeReference expectedType) at offset 54
at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteComplexProperties(Microsoft.AspNet.OData.Formatter.Serialization.SelectExpandNode selectExpandNode, Microsoft.AspNet.OData.ResourceContext resourceContext, Microsoft.OData.ODataWriter writer) at offset 94
at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer.WriteResource(System.Object graph, Microsoft.OData.ODataWriter writer, Microsoft.AspNet.OData.Formatter.Serialization.ODataSerializerContext writeContext, Microsoft.OData.Edm.IEdmTypeReference expectedType) at offset 119
at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSet(System.Collections.IEnumerable enumerable, Microsoft.OData.Edm.IEdmTypeReference resourceSetType, Microsoft.OData.ODataWriter writer, Microsoft.AspNet.OData.Formatter.Serialization.ODataSerializerContext writeContext) at offset 163
at Microsoft.AspNet.OData.Formatter.ODataOutputFormatterHelper.WriteToStream(System.Type type, System.Object value, Microsoft.OData.Edm.IEdmModel model, Microsoft.OData.ODataVersion version, System.Uri baseAddress, System.Net.Http.Headers.MediaTypeHeaderValue contentType, Microsoft.AspNet.OData.Interfaces.IWebApiUrlHelper internaUrlHelper, Microsoft.AspNet.OData.Interfaces.IWebApiRequestMessage internalRequest, Microsoft.AspNet.OData.Interfaces.IWebApiHeaders internalRequestHeaders, System.Func`2 getODataMessageWrapper, System.Func`2 getEdmTypeSerializer, System.Func`2 getODataPayloadSerializer, System.Func`1 getODataSerializerContext) at offset 603
at Microsoft.AspNet.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(System.Type type, System.Object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext, System.Threading.CancellationToken cancellationToken) at offset 349
at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__22.MoveNext() at offset 144
When my expand query was missing results where my nullable Id was actually null, it turned out I had not created my code-first migrations correctly. I had used Guid
instead of Guid?
.
The generated migration was using NOT NULL DEFAULT '000-...
, so the error somehow escaped DB detection. Only showed up on my API Odata calls
I have implemented expand with odata v4 using .net core. It is working fine but creating issue when not able to find key property in expand. Property is nullable hence EF applying left join to it. To confirm the same I also checked query in SQL profiler and that query is also working fine in SQL Server.
If I am passing all the property as select it is working fine. Let's take one example.
Model: Product{ public int prodid {get,set}; public string prodname {get,set}; public string depid {get,set}; public Department department{get,set}; }
Department{ public string depid {get,set}; public string depname{get set} }
working Expand odata url: http://localhost:5655/prod?$count=true$select=prodid$expand=department($select=depid,depname) value: { prodid:1 Department: { depid:133 depname:null } } Not working Expand odata url:
http://localhost:5655/prod?$count=true$select=prodid$expand=department Giving 500 internal server error
Data in DB: Product table: prodid |depid |prodname 1 |133 |test
No data available in Department table for depid 133
Runtime in profiler it is generating below query: select p.prodid,p.depid,t.depid, t.depname from (select p.prodid,p.prodname from product p) as [p] left join ( select d.depid, d.depname from department d) as [t] on p.depid=t.depid result: prodid |p.depid |t.depid |t.depname 1 |133 |null |null