Functions that return entity types with properties that are contained entity sets throw exception when invoked.
EmailAddress class with property [Key] Address : stringUser class with properties Id : int and [Contained] EmailAddresses : ICollection<EmailAddress>UserUser with a parameter address : string, returning a single entity from entity set _Users_address set to an existing e-mail address.A single entity of type User.
An exception:
{
"error": {
"code": "",
"message": "An error has occurred.",
"innererror": {
"message": "The Path property in ODataMessageWriterSettings.ODataUri must be set when writing contained elements.",
"type": "Microsoft.OData.Core.ODataException",
"stacktrace": " at Microsoft.OData.Core.ODataWriterCore.EnterScope(WriterState newState, ODataItem item)\ \ at Microsoft.OData.Core.ODataWriterCore.WriteStartNavigationLinkImplementation(ODataNavigationLink navigationLink)\ \ at Microsoft.OData.Core.ODataWriterCore.WriteStart(ODataNavigationLink navigationLink)\ \ at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteNavigationLinks(IEnumerable`1 navigationProperties, EntityInstanceContext entityInstanceContext, ODataWriter writer)\ \ at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteEntry(Object graph, ODataWriter writer, ODataSerializerContext writeContext)\ \ at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)\ \ at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)\ \ at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)\ \ at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\ \ --- End of stack trace from previous location where exception was thrown ---\ \ at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\ \ at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\ \ at System.Web.Http.Owin.HttpMessageHandlerAdapter.<BufferResponseContentAsync>d__13.MoveNext()"
}
}
}
var modelBuilder = new ODataConventionModelBuilder();
var userType = modelBuilder.EntityType<User>();
var byEmailAddressFunction = userType.Collection.Function("byEmailAddress");
byEmailAddressFunction.Parameter<string>("address");
byEmailAddressFunction.ReturnsFromEntitySet<User>("Users");
modelBuilder.EntitySet<User>("Users");
public class User
{
public int Id { get; set; }
[Contained]
public ICollection<EmailAddress> EmailAddresses { get; set; }
}
public class EmailAddress
{
[Key]
public string Address { get; set; }
}
[HttpGet]
public IHttpActionResult ByEmailAddress(string address)
{
if (address == null)
{
return BadRequest();
}
var user = _users.FirstOrDefault(u =>
u.EmailAddresses.Any(a =>
a.Address.Equals(address, StringComparison.InvariantCulture)));
if (user == null)
{
return NotFound();
}
return Ok(user);
}
I created a fork and a branch with a fix to this issue: https://github.com/vojtechvit/odata.net/commits/bugfix/containedentityinfunction
_However - I'm not fully aware of the internal mechanics of the complex ODataWriterCore class, so please consider this code just as a highlight of the problem area!_
For instance, I'm not persuaded that it generates all correct path segments (e.g. entity key segments are missing).
It would be great if you could include the bugfix already in the 5.5 release! O:-)
Is there an ETA for this fix?
ODataMediaTypeFormatter has codes as:
``` C#
writerSettings.ODataUri = new ODataUri
{
ServiceRoot = baseAddress,
// TODO: 1604 Convert webapi.odata's ODataPath to ODL's ODataPath, or use ODL's ODataPath.
SelectAndExpand = Request.ODataProperties().SelectExpandClause,
Path = (path == null || IsOperationPath(path)) ? null : path.ODLPath,
};
Where `IsOperationPath(path)` returns `true` because it's a function call, therefore `Path` in the `writerSettings.ODataUri` is null.
While, in ODL, `ODataWriterCore` need `Path` in the `writerSettings.ODataUri` to be set, otherwise it will throw the ODataException. However, it's hard to calculate the path for the returned entity of a function.
Therefore, It's better to set the path by yourself (the developer) in the controller. Below's the workaround and it can work at my side:
``` C#
[HttpGet]
public IHttpActionResult ByEmailAddress(string address)
{
if (address == null)
{
return BadRequest();
}
var user = _users.FirstOrDefault(u =>
u.EmailAddresses.Any(a =>
a.Address.Equals(address, StringComparison.InvariantCulture)));
if (user == null)
{
return NotFound();
}
IEdmModel model = Request.ODataProperties().Model;
ODataPath path = new DefaultODataPathHandler().Parse(model, "http://localhost:44567/odata/", "Users(1)");
Request.ODataProperties().Path = path;
return Ok(user);
}
Please replace the service root and odataPath parameter values as your own string.
Please re-open it if you have further concern.
Using the new libraries would require something like:
```c#
var path = new DefaultODataPathHandler().Parse("http://localhost:44567/odata/v1", "MyEntityRoute", Request.GetRequestContainer());
Request.ODataProperties().Path = path;
```
+1 for a fix for this.
I run into this issue whenever I try to return a newly created entity set record from an unbound action.
+1. Is there an ETA for a fix? This issue is repro'ing for me despite the Path being set in Request.ODataProperties() with the correct value.
+1. is there any ETA for this fix?
Thanks for the workaround.
Setting Request.ODataProperties().Path worked for me.
+1 for fixing the issue or providing some documentation so engineers are aware of this behavior.
I am also failing into this issue :(
@xuzhg, can I ask your attention please.
@vojtechvit created a pull request for fixing this issue in odata.net repo
https://github.com/OData/odata.net/pull/111
I don't know why this pull request is not merged and just closed, I saw the code and it looks good for me.
Can you spent some time on this issue?
This issue is still actual in Microsoft.AspNetCore.OData 7.0.1.
Im also facing this issue
also still facing this bug on Microsoft.AspNetCore.OData 7.0.1.
But still workaround works for me in Microsoft.AspNetCore.OData 7.0.1
var path = Request.GetPathHandler().Parse("http://localhost/odata/", "users(1)", request.GetRequestContainer());
Request.ODataFeature().Path = path;
Also facing this one here!
also facing this!
Whow. 3 years open and still not fixed. Meanwhile anyone has a decent solution for dotnet core? I just added a contained entity set and boom - "unrelated" functionality blowing.
Request.ODataProperties().Path does not seem to be available in Microsoft.AspNetCore.OData.
Could you please advise?
As above, the workaround for .net core is:
var path = Request.GetPathHandler().Parse("http://localhost/odata/", "users(1)", request.GetRequestContainer());
Request.ODataFeature().Path = path;
Running now also into this. A higher priorisation of this bug would be greatly appreciated.
@crackalak does your workaround still work for you?
My code looks like this, but fails:
public class ClientsController : ODataController
{
private readonly DataContext ctx;
public ClientsController(
DataContext ctx
)
{
this.ctx = ctx;
}
[EnableQuery]
public IQueryable<Client> Get(string search)
{
return ctx.Clients;
}
[EnableQuery]
public SingleResult<Client> Get([FromODataUri] Guid key)
{
IQueryable<Client> result = ctx.Clients.Where(p => p.Id == key);
return SingleResult.Create(result);
}
[EnableQuery]
[HttpGet]
public IEnumerable<Client> TestData()
{
var path = Request.GetPathHandler().Parse("https://localhost:5001/odata/", "clients/TestData", Request.GetRequestContainer());
Request.ODataFeature().Path = path;
return new FakeClients().Get();
}
}
FYI for anyone coming across this in late 2020, looks like this has been fixed in WebAPI 7.5.0 (https://docs.microsoft.com/en-us/odata/changelog/webapi-7x) by PR 2060
Most helpful comment
Using the new libraries would require something like:
```c#
var path = new DefaultODataPathHandler().Parse("http://localhost:44567/odata/v1", "MyEntityRoute", Request.GetRequestContainer());
Request.ODataProperties().Path = path;
```