OData / OData/AspNetCoreOData

InvalidOperationException when returning ODataController.Created in a custom bound action

Open
#179 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
505
Forks
186
PR merge metrics
No merged PRs in 30d

Description

Issue description

Hi Sam, I have a custom OData action bound with the namespace and return type properly setup. However, I noticed that using the ODataController.Created return instead of Ok return in my controller implementation causes the below InvalidOperationException. I expected that Created is the more correct return type; however, using Ok appears to work just as well and all the odata query parameters appear to work still as expected (e.g. $select, $expand, etc.).

System.InvalidOperationException: The related entity set or singleton cannot be found from the OData path. The related entity set or singleton is required to serialize the payload.
   at Microsoft.AspNetCore.OData.Results.ResultHelpers.GenerateODataLink(HttpRequest request, Object entity, Boolean isEntityId) in Microsoft.AspNetCore.OData.dll:token 0x60002b5+0x43
   at Microsoft.AspNetCore.OData.Results.CreatedODataResult`1.GenerateLocationHeader(HttpRequest request) in Microsoft.AspNetCore.OData.dll:token 0x60002a6+0x0
   at Microsoft.AspNetCore.OData.Results.CreatedODataResult`1.ExecuteResultAsync(ActionContext context) in Microsoft.AspNetCore.OData.dll:token 0x60002a3+0x86
...

I tried to see if any of your sample code uses Created for custom actions but it looks like the only custom action implementation present uses Ok instead and this code has been commented out. For example, I saw the custom action IncreaseSalary in the sample but the test code always used Ok such as the following:
https://github.com/OData/AspNetCoreOData/blob/ce38df9ebc73575e103aa73544a4cfbb6e9d5814/test/Microsoft.AspNetCore.OData.E2E.Tests/BoundOperation/BoundOperationController.cs#L345

Relevant code setup below

IEdmModel setup:

private static IEdmModel GetEdmModel()
{
	var builder = new ODataConventionModelBuilder();

	var cardEntitySet = builder.EntitySet<Card>("Card");
	var pageEntitySet = builder.EntitySet<Page>("Page");

	var cardAddPage = cardEntitySet.EntityType.Action(nameof(CardController.AddPage));
	cardAddPage.Namespace = cardEntitySet.EntityType.Name;
	cardAddPage.ReturnsFromEntitySet(pageEntitySet);
	
	...
}

The controller implementation:

public class CardController : ODataController
{

	[EnableQuery]
	[HttpPost("api/v1/Card({key})/Card.AddPage")]
	[HttpPost("api/v1/Card({key})/Card.AddPage()")]
	public async Task<IActionResult> AddPage(int key)
	{
		var card = await DbContext.Card.Include(e => e.Pages).FirstOrDefaultAsync(e => e.Id == key);
		if (card == null) return NotFound(key);
		var page = new Page();
		card.Pages.Add(page);
		await DbContext.SaveChangesAsync();
		
		// Using ODataController.Created below causes the InvalidOperationException
		// return Created(page);
		
		// Workaround:
		return Ok(page);
	}

}

The above custom action shows up as I would expect in the $metadata page (return type specified correctly to the right entityset) and in the debug $odata page (enabled via UseODataRouteDebug) as being correctly setup underneath the OData Endpoint Mapping table (as opposed to being listed under the Non-OData Endpoint Mapping table).

Version information

  • net6.0 preview 3 (6.0.100-preview.3.21202.5)
  • Microsoft.AspNetCore.OData 8.0.0-Nightly202105211212

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the custom Card.AddPage action using Created(page) and compare it with the Ok(page) workaround. Read the E2D bound-operation example in test/Microsoft.AspNetCore.OData.E2E.Tests/BoundOperation/BoundOperationController.cs around line 345, then trace the ODataController Created result and route metadata; done means the action returns successfully with the expected entity payload and OData query options.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.