Odata not working with Edm model loaded from file
@xuzhg is already working on this.
Since Apr 25, 2023.
- Dominant language
- C#
- Stars
- 505
- Forks
- 186
- PR merge metrics
- No merged PRs in 30d
Description
Assemblies affected
ASP.NET Core OData 8.x
Describe the bug
When trying to read a edm model from a file, Odata stops working.
Reproduce steps
Create two entity:
public class User
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Book
{
public string Id { get; set; }
public User Author { get; set; }
}
Create two controllers:
public class BooksController : ODataController
{
[EnableQuery]
public IActionResult Get()
{
return Ok(new[] { new Book { Id = "1" }, new Book { Id = "2" } });
}
[EnableQuery]
public IActionResult Get(string key)
{
return Ok(new Book { Id = key });
}
}
and
public class UsersController : ODataController
{
[EnableQuery]
public IActionResult Get()
{
return Ok(new[] { new User { Id = "1" }, new User { Id = "2" } });
}
[EnableQuery]
public IActionResult Get(string key)
{
return Ok(new User { Id = key });
}
}
Startup class:
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
this.Env = env;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Env { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services
.AddMvc(o =>
{
o.EnableEndpointRouting = false;
})
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.AllowTrailingCommas = true;
options.JsonSerializerOptions.IgnoreNullValues = true;
options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString;
});
services.AddControllers()
.AddApplicationPart(Assembly.GetExecutingAssembly())
.AddOData((setupAction, serviceProvider) =>
{
setupAction.EnableAttributeRouting = true;
setupAction.RouteOptions.EnableUnqualifiedOperationCall = true;
setupAction.RouteOptions.EnableQualifiedOperationCall = true;
setupAction.RouteOptions.EnableKeyAsSegment = true;
setupAction.RouteOptions.EnableKeyInParenthesis = true;
setupAction.QuerySettings.EnableCount = true;
setupAction.QuerySettings.EnableSelect = true;
setupAction.QuerySettings.EnableExpand = true;
setupAction.QuerySettings.EnableSkipToken = true;
setupAction.QuerySettings.EnableFilter = true;
setupAction.QuerySettings.EnableOrderBy = true;
setupAction.QuerySettings.MaxTop = null;
setupAction.AddRouteComponents("api", GetEdmModel(), serviceCollection =>
{
});
});
services.AddHealthChecks();
services.AddRouting();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseMvc();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseHttpsRedirection();
app.UseHealthChecks("/health");
}
private IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "MyNamespace";
builder.EntitySet<User>("Users");
builder.EntitySet<Book>("Books");
var edmModel = builder.GetEdmModel();
SaveEdmModel(edmModel);
return edmModel;
}
private void SaveEdmModel(IEdmModel model)
{
using (var writer = XmlWriter.Create("edmModel.xml"))
{
model.TryWriteSchema(writer, out var errors);
}
}
}
After that, both entities must be registered and we can run the project and request books or a book by key.


Everything is working.
When we first started the project, we saved the schema to a file.
Let's change the GetEdmModel() method to read from a file
private IEdmModel GetEdmModel()
{
using (var reader = XmlReader.Create("edmModel.xml"))
{
var readers = new[] { reader };
SchemaReader.TryParse(readers, out var edmModel, out var errors);
return edmModel;
}
}
Let's restart the project and try to execute the same queries.

We can see that odata.context is missing.

The same situation
And if we trying to expand author, then we will receive new error:
URI: https://localhost:44387/api/Books/2?$expand=Author
Microsoft.OData.ODataException: A type named 'OdataEdmModelBug.Models.Book' could not be resolved by the model. When a model is available, each type name must resolve to a valid type.
at Microsoft.OData.TypeNameOracle.ResolveAndValidateTypeName(IEdmModel model, String typeName, EdmTypeKind expectedTypeKind, Nullable`1 expectStructuredType, IWriterValidator writerValidator)
at Microsoft.OData.TypeNameOracle.ResolveAndValidateTypeFromTypeName(IEdmModel model, IEdmStructuredType expectedType, String typeName, IWriterValidator writerValidator)
at Microsoft.OData.ODataWriterCore.GetResourceType(ODataResourceBase resource)
at Microsoft.OData.ODataWriterCore.ValidateResourceForResourceSet(ODataResourceBase resource, ResourceBaseScope resourceScope)
at Microsoft.OData.ODataWriterCore.<>c.<<WriteStartResourceImplementationAsync>b__194_0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.OData.ODataWriterCore.InterceptExceptionAsync[TArg0](Func`3 action, TArg0 arg0)
at Microsoft.OData.ODataWriterCore.WriteStartResourceImplementationAsync(ODataResource resource)
at Microsoft.OData.ODataWriterCore.WriteStartAsync(ODataResource resource)
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.ODataResourceSerializer.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|29_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.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware: Warning: The response has already started, the error page middleware will not be executed.
Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer: Error: Connection ID "18302628903618874841", Request ID "40000ddb-0004-fe00-b63f-84710c7967bb": An unhandled exception was thrown by the application.
Microsoft.OData.ODataException: A type named 'OdataEdmModelBug.Models.Book' could not be resolved by the model. When a model is available, each type name must resolve to a valid type.
at Microsoft.OData.TypeNameOracle.ResolveAndValidateTypeName(IEdmModel model, String typeName, EdmTypeKind expectedTypeKind, Nullable`1 expectStructuredType, IWriterValidator writerValidator)
at Microsoft.OData.TypeNameOracle.ResolveAndValidateTypeFromTypeName(IEdmModel model, IEdmStructuredType expectedType, String typeName, IWriterValidator writerValidator)
at Microsoft.OData.ODataWriterCore.GetResourceType(ODataResourceBase resource)
at Microsoft.OData.ODataWriterCore.ValidateResourceForResourceSet(ODataResourceBase resource, ResourceBaseScope resourceScope)
at Microsoft.OData.ODataWriterCore.<>c.<<WriteStartResourceImplementationAsync>b__194_0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.OData.ODataWriterCore.InterceptExceptionAsync[TArg0](Func`3 action, TArg0 arg0)
at Microsoft.OData.ODataWriterCore.WriteStartResourceImplementationAsync(ODataResource resource)
at Microsoft.OData.ODataWriterCore.WriteStartAsync(ODataResource resource)
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.ODataResourceSerializer.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|29_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.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
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.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()
EDM (CSDL) Model
<?xml version="1.0" encoding="utf-8"?>
<Schema Namespace="MyNamespace" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="User">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
</EntityType>
<EntityType Name="Book">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false" />
<NavigationProperty Name="Author" Type="MyNamespace.User" />
</EntityType>
<EntityContainer Name="Container">
<EntitySet Name="Users" EntityType="MyNamespace.User" />
<EntitySet Name="Books" EntityType="MyNamespace.Book">
<NavigationPropertyBinding Path="Author" Target="Users" />
</EntitySet>
</EntityContainer>
</Schema>
Expected behavior
The Edm model is loaded and all odata functions are working as expected.
Notes
The same behaviour using CsdlReader and CsdlWriter.
$metadata endpoint works after loading through file.
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.