The EDM instance of type '[type Nullable=True]' is missing the property 'id'.
@xuzhg is already working on this.
Since Apr 18, 2023.
- Dominant language
- C#
- Stars
- 505
- Forks
- 186
- PR merge metrics
- No merged PRs in 30d
Description
Assemblies affected
Microsoft.AspNetCore.OData Version="8.1.1"
Describe the bug
I'm getting:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The EDM instance of type '[ODataError.Domain.Taxonomies.Country Nullable=True]' is missing the property 'id'.
at Microsoft.AspNetCore.OData.Formatter.ResourceContext.GetPropertyValue(String propertyName)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateStructuralProperty(IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateStructuralPropertyBag(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteResourceAsync(Object graph, ODataWriter writer, ODataSerializerContext writeContext, IEdmTypeReference expectedType)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteObjectInlineAsync(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSetAsync(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer, ODataSerializerContext writeContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectInlineAsync(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectAsync(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
at Microsoft.AspNetCore.OData.Formatter.ODataOutputFormatterHelper.WriteToStreamAsync(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, HttpRequest request, IHeaderDictionary requestHeaders, IODataSerializerProvider serializerProvider)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.OData.Query.ODataQueryRequestMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.OData.Routing.ODataRouteDebugMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Reproduce steps
I made a small project where I include the minimum necessary of my code to reproduce it:
- download ODataError.zip
- Make a request to
[GET] https://localhost:7066/odata/v1/countries
Data Model
public abstract class Entity<TId>
{
[Key]
public TId Id { get; set; }
public override bool Equals(object obj) {...}
public override int GetHashCode() {...}
public static bool operator ==(Entity<TId> a, Entity<TId> b) {...}
public static bool operator !=(Entity<TId> a, Entity<TId> b) {...}
}
public abstract class AuditableEntity<T> : Entity<T>
{
public AuditableEntity() {}
public AuditableEntity(T id) {...}
public DateTimeOffset CreatedOn { get; set; }
public Guid CreatedById { get; set; }
public string CreatedByDisplayName { get; set; }
public DateTimeOffset? ModifiedOn { get; set; }
public Guid? ModifiedById { get; set; }
public string ModifiedByDisplayName { get; set; }
public DateTime Timestamp { get; set; }
public virtual void UpdateCreationMetadata(Guid? userId = null, string displayName = null, DateTimeOffset? createdOn = null) {...}
public virtual void UpdateModificationMetadata(Guid? userId = null, string displayName = null, DateTimeOffset? updatedOn = null) {...}
}
public class Country : AuditableEntity<int>
{
[JsonConstructor]
public Country() {...}
public Country(int id, string name, string iso2, string iso3, string phoneCode) : base(id) {...}
public string Name { get; set; }
public string Iso2 { get; set; }
public string Iso3 { get; set; }
public string PhoneCode { get; set; }
}
public class Language : AuditableEntity<int>
{
[JsonConstructor]
public Language() {...}
public Language(int id, string name, string code) : base(id) {...}
public string Name { get; set; }
public string Code { get; set; }
}
public class Methodology : AuditableEntity<Guid>
{
[JsonConstructor]
public Methodology() {...}
public Methodology(Guid id, string name) : base(id) {...}
public string Name { get; set; }
}
EDM (CSDL) Model
<?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="ODataError.Domain.Taxonomies" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Country">
<Key>
<PropertyRef Name="id" />
</Key>
<Property Name="id" Type="Edm.Int32" Nullable="false" />
<Property Name="name" Type="Edm.String" />
<Property Name="iso2" Type="Edm.String" />
<Property Name="iso3" Type="Edm.String" />
<Property Name="phoneCode" Type="Edm.String" />
<Property Name="createdOn" Type="Edm.DateTimeOffset" Nullable="false" />
<Property Name="createdById" Type="Edm.Guid" Nullable="false" />
<Property Name="createdByDisplayName" Type="Edm.String" />
<Property Name="modifiedOn" Type="Edm.DateTimeOffset" />
<Property Name="modifiedById" Type="Edm.Guid" />
<Property Name="modifiedByDisplayName" Type="Edm.String" />
<Property Name="timestamp" Type="Edm.DateTimeOffset" Nullable="false" />
</EntityType>
<EntityType Name="Language">
<Key>
<PropertyRef Name="id" />
</Key>
<Property Name="id" Type="Edm.Int32" Nullable="false" />
<Property Name="name" Type="Edm.String" />
<Property Name="code" Type="Edm.String" />
<Property Name="createdOn" Type="Edm.DateTimeOffset" Nullable="false" />
<Property Name="createdById" Type="Edm.Guid" Nullable="false" />
<Property Name="createdByDisplayName" Type="Edm.String" />
<Property Name="modifiedOn" Type="Edm.DateTimeOffset" />
<Property Name="modifiedById" Type="Edm.Guid" />
<Property Name="modifiedByDisplayName" Type="Edm.String" />
<Property Name="timestamp" Type="Edm.DateTimeOffset" Nullable="false" />
</EntityType>
<EntityType Name="Methodology">
<Key>
<PropertyRef Name="id" />
</Key>
<Property Name="id" Type="Edm.Guid" Nullable="false" />
<Property Name="name" Type="Edm.String" />
<Property Name="createdOn" Type="Edm.DateTimeOffset" Nullable="false" />
<Property Name="createdById" Type="Edm.Guid" Nullable="false" />
<Property Name="createdByDisplayName" Type="Edm.String" />
<Property Name="modifiedOn" Type="Edm.DateTimeOffset" />
<Property Name="modifiedById" Type="Edm.Guid" />
<Property Name="modifiedByDisplayName" Type="Edm.String" />
<Property Name="timestamp" Type="Edm.DateTimeOffset" Nullable="false" />
</EntityType>
</Schema>
<Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="Container">
<EntitySet Name="Countries" EntityType="ODataError.Domain.Taxonomies.Country" />
<EntitySet Name="Languages" EntityType="ODataError.Domain.Taxonomies.Language" />
<EntitySet Name="Methodologies" EntityType="ODataError.Domain.Taxonomies.Methodology" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
model built using:
public static IEdmModel GetODataModel()
{
// Create the service EDM model.
var modelBuilder = new ODataConventionModelBuilder()
.EnableLowerCamelCase();
modelBuilder.EntitySet<Country>("Countries");
modelBuilder.EntityType<Country>().HasKey(x => x.Id);
modelBuilder.EntitySet<Language>("Languages");
modelBuilder.EntityType<Language>().HasKey(x => x.Id);
modelBuilder.EntitySet<Methodology>("Methodologies");
modelBuilder.EntityType<Methodology>().HasKey(x => x.Id);
return modelBuilder.GetEdmModel();
}
controller example:
[ApiController]
[Route("api/v1/{controller}")]
[AllowAnonymous]
public class CountriesController : ODataController
{
List<Country> _countries = new List<Country> { new Country(1, "spain", "sp", "spa", "34"), new Country(2, "usa", "us", "usa", "1") };
public CountriesController()
{
}
[HttpGet]
[EnableQuery]
public async Task<ActionResult<IQueryable<Country>>> Get()
{
return Ok(_countries);
}
[HttpGet("{key}")]
public async Task<ActionResult<Country>> Get([FromODataUri] int key)
{
var result = _countries.FirstOrDefault(x => x.Id == key);
if (result is object) return result;
return NotFound();
}
}
Request/Response
[GET] https://localhost:7066/odata/v1/countries
System.InvalidOperationException: The EDM instance of type '[ODataError.Domain.Taxonomies.Country Nullable=True]' is missing the property 'id'.
at Microsoft.AspNetCore.OData.Formatter.ResourceContext.GetPropertyValue(String propertyName)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateStructuralProperty(IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateStructuralPropertyBag(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteResourceAsync(Object graph, ODataWriter writer, ODataSerializerContext writeContext, IEdmTypeReference expectedType)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSerializer.WriteObjectInlineAsync(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSetAsync(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer, ODataSerializerContext writeContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectInlineAsync(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
at Microsoft.AspNetCore.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectAsync(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
at Microsoft.AspNetCore.OData.Formatter.ODataOutputFormatterHelper.WriteToStreamAsync(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, HttpRequest request, IHeaderDictionary requestHeaders, IODataSerializerProvider serializerProvider)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.OData.Query.ODataQueryRequestMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.OData.Routing.ODataRouteDebugMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware.InvokeAsync(HttpContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
Expected behavior
the odata response (like [GET] https://localhost:7066/odata/v1/languages)
Additional context
First I added Country and it worked, then I added Language and Methodology and it stopped working. Looking at other issues on the internet (like https://github.com/OData/WebApi/issues/1625) I think it is related to the use of .EnableLowerCamelCase() and .HasKey(x => x.Id).
this doesnt work:
public static IEdmModel GetODataModel()
{
// Create the service EDM model.
var modelBuilder = new ODataConventionModelBuilder()
.EnableLowerCamelCase();
modelBuilder.EntitySet<Country>("Countries");
modelBuilder.EntityType<Country>().HasKey(x => x.Id);
modelBuilder.EntitySet<Language>("Languages");
modelBuilder.EntityType<Language>().HasKey(x => x.Id);
modelBuilder.EntitySet<Methodology>("Methodologies");
modelBuilder.EntityType<Methodology>().HasKey(x => x.Id);
return modelBuilder.GetEdmModel();
}
this work (but there are entities missing as expected):
public static IEdmModel GetODataModel()
{
// Create the service EDM model.
var modelBuilder = new ODataConventionModelBuilder()
.EnableLowerCamelCase();
modelBuilder.EntitySet<Country>("Countries");
modelBuilder.EntityType<Country>().HasKey(x => x.Id);
/*modelBuilder.EntitySet<Language>("Languages");
modelBuilder.EntityType<Language>().HasKey(x => x.Id);
modelBuilder.EntitySet<Methodology>("Methodologies");
modelBuilder.EntityType<Methodology>().HasKey(x => x.Id);*/
return modelBuilder.GetEdmModel();
}
this work (but I like to define here my entities and do not rely on conventions on the model class):
public static IEdmModel GetODataModel()
{
// Create the service EDM model.
var modelBuilder = new ODataConventionModelBuilder()
.EnableLowerCamelCase();
modelBuilder.EntitySet<Country>("Countries");
//modelBuilder.EntityType<Country>().HasKey(x => x.Id);
modelBuilder.EntitySet<Language>("Languages");
//modelBuilder.EntityType<Language>().HasKey(x => x.Id);
modelBuilder.EntitySet<Methodology>("Methodologies");
//modelBuilder.EntityType<Methodology>().HasKey(x => x.Id);
return modelBuilder.GetEdmModel();
}
this work (but I need to use lower case):
public static IEdmModel GetODataModel()
{
// Create the service EDM model.
var modelBuilder = new ODataConventionModelBuilder();
// .EnableLowerCamelCase();
modelBuilder.EntitySet<Country>("Countries");
modelBuilder.EntityType<Country>().HasKey(x => x.Id);
modelBuilder.EntitySet<Language>("Languages");
modelBuilder.EntityType<Language>().HasKey(x => x.Id);
modelBuilder.EntitySet<Methodology>("Methodologies");
modelBuilder.EntityType<Methodology>().HasKey(x => x.Id);
return modelBuilder.GetEdmModel();
}
[GET] https://localhost:7066/odata/v1/languages and [GET] https://localhost:7066/odata/v1/methodologies work but [GET] https://localhost:7066/odata/v1/countries doesnt
Screenshots
from https://localhost:7066/$odata


Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.