Open Sneedd opened 2 years ago
The only workaround I found so far is returning nothing and react in my OData client accordingly, here the solution for the sample project.
// Override the services
options.AddRouteComponents("odata", builder.GetEdmModel(), (services) =>
{
services.AddSingleton<ODataPrimitiveSerializer, ODataPrimitiveSerializer2>();
services.AddSingleton<ODataResourceSerializer, ODataResourceSerializer2>();
});
internal class ODataResourceSerializer2 : ODataResourceSerializer
{
public ODataResourceSerializer2(IODataSerializerProvider serializerProvider) : base(serializerProvider) { }
public override Task WriteObjectAsync(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
{
if (graph == null)
{
return Task.CompletedTask;
}
return base.WriteObjectAsync(graph, type, messageWriter, writeContext);
}
}
public class ODataPrimitiveSerializer2 : ODataPrimitiveSerializer
{
public ODataPrimitiveSerializer2() { }
public override Task WriteObjectAsync(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
{
if (graph == null)
{
return Task.CompletedTask;
}
return base.WriteObjectAsync(graph, type, messageWriter, writeContext);
}
}
This currently works for me, but the solution is not optimal.
Or changing everything to IActionResult
which would be a major change for me.
Hope someone have a better workaround.
@Sneedd do you have nullable reference types enabled in the project? Does it make a difference if you declare the return value as Article?
instead of Article
? Like:
.ReturnsFromEntitySet<Article?>("Articles");
@julealgon I tried it now with Article?
and changed the Nullable setting of the project but it did not make a difference.
I also tried to find the example/documentation where I got this information (to send a actual value instead of IActionResult
), but with no luck. So my question would be: Should both following examples work? And if not wouldn't it be better to throw an exception.
[HttpGet]
public string GetName() => null; // Does not work
[HttpGet]
public IActionResult GetName() => this.NoContent(); // Works
Btw, I started to use the IActionResult
approach, because my previous mentioned workaround did not felt good after some more testing.
@Sneedd I know this doesn't answer your original question, but is there a strong reason you want to return null
, instead of returning a 404 which is the standard behavior for "when a resource is not found"? Are you using the null
value with this semantic, and if so why?
@Sneedd can you give this a shot?
.ReturnsFromEntitySet<Article>("Articles").ReturnNullable = true;
@julealgon Thanks for your reply and sorry for my late reply. I return 404 but only when working with the resource endpoints and an entity is missing. But the operation endpoints for OData action and functions are different, I only provide a abstraction a simple API for someone how want use this feature. Because only I am implementing new functions and actions, I wanted to give the other developers the freedom to send null
. That said I already starting to change the underlying behavior to IActionResult
return values.
.ReturnsFromEntitySet<Article>("Articles").ReturnNullable = true;
I already tried this and found out that ReturnsFromEntitySet()
sets ReturnNullable
already to true, so nothing different here.
Because the behavior in the initial comment cannot be correct and is easily reproduced, I thought I should report it. Like I said, I have a workaround with which I am happy.
Thanks for clarifying @Sneedd . Now I wonder if this is an actually supported scenario or not. If ReturnNullable
is already true
by default, this could very well mean that entities just can't ever be null?
Will leave it up to someone on the team to check this.
Regardless, I think the user experience here is pretty bad. IMHO:
ReturnNullable
should be false
by default if entities can't be nullReturnNullable
to true
if it is not supported for entities should throw a specific exceptionSo even if there is no bug here, I think there are changes to be done.
According to the OData V4 spec, the return value for a primitive (i.e., string) function returning a null value should be 204 (as per the error in the Kestrel example above). In OData V3, we returned the #Edm.Null value. There was a bug in early versions of the OData V4 library that we continued to return this invalid null value in OData V4 and we fixed it, but we added a LibraryCompatibilityFlag for backward compatibility to support existing services that may have clients receiving that value. So, I'm surprised this value is getting returned (even invalidly) without somehow setting that compatibility flag.
ReturnNullable=true means that the function/action should be able to return a null value. The way that the null value is represented in OData V4 is as a 204/404 (depending on whether or not it is primitive).
So, if I understand the issue, the request is that the controller method shouldn't have to understand the protocol -- it should just be able to return the null value and have the framework do the right thing (return 204). That seems a reasonable separation of having controller method implementing the data logic and the framework understanding how that is represented in the protocol.
ReturnNullable=true means that the function/action should be able to return a null value. The way that the null value is represented in OData V4 is as a 204/404 (depending on whether or not it is primitive).
Interesting, thanks for sharing.
So, if I understand the issue, the request is that the controller method shouldn't have to understand the protocol -- it should just be able to return the null value and have the framework do the right thing (return 204).
Feels to me like this is an option, but one could also at least improve the exception message in the entity-based cases above. Contrary to the primitive value exception which has a very descriptive message that leads people into the correct action, the entity exception is extremely generic and has no explanation about what is happening.
Guiding people into using the proper http response by updating that exception message would result in less "magic" in the framework. That can sometimes be seen as a good thing too.
Hello, I encountered ome problems when returning null in OData operations (actions and functions). I cannot provide my actual code, so I made a sample project which result in similar errors.
Here the sample code using Microsoft.AspNetCore.OData 8.0.10 followed with some of the errors which I encountered with this sample project.
Calling the
GetEntity(): Article
function results in the following exception and same with theGetEntityAction(): Article
:Calling the
GetName(): string
function results in the following response:Note the missing end bracket? Also I would expect
"value": null
or like the exception said a 204. This happends when using the IISExpress profile. Using the Kestrel profile does not return a response, which is easily reproduced with WireShark.