OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
857 stars 473 forks source link

Support constructors (nullable reference type enabled models) #2110

Open snebjorn opened 4 years ago

snebjorn commented 4 years ago

Enabling nullable reference types and following the model guidelines provided by the EF Core team breaks model creation in OData.

Assemblies affected

Reproduce steps

Make a model according to the guidelines, using a constructor and leaving out navigation properties.

public class Blog
{
    public Blog(int id, string name, string author)
    {
        Id = id;
        Name = name;
        Author = author;
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Author { get; set; }

    public ICollection<Post> Posts { get; } = new List<Post>();
}

Bonus Having a private setter also causes issues. I had a private setter on a key (Id) as it wasn't a generated key. But then the model builder complained about a missing key. Manually using .HasKey(e => e.Id) removed the error but no idea if it works runtime.

Now call your endpoint. I tried to POST

[HttpPost]
public async Task<IActionResult> Post([FromBody] Blog blog)

Expected result

I expected it to correctly serialize the posted json into a Blog object.

Actual result

The blog parameter is null.

return BadRequest(ModelState); returns the below message.

"message": "The input was not valid.\r\n\r\nblog:\r\nThe blog field is required.",

Funny "blog field"... it's not a field it's the constructor

Additional detail

It's clearly the JsonNet serializer that fails. So I tried to add [JsonConstructor] above the constructor but to no avail. According to the example it should do the trick. That being constructor arguments and private setters. https://www.newtonsoft.com/json/help/html/JsonConstructorAttribute.htm

snebjorn commented 4 years ago

I believe I've narrowed it down to OData not supporting models with constructors and I cannot find a way to configure Json.Net inside OData to enable support.

Json.Net supports deserializing models with non-default constructors, and standard ASP.NET Core 3.1 api controllers when using Microsoft.AspNetCore.Mvc.NewtonsoftJson also works out of the box with non-default constructors. So OData must have disabled it.

snebjorn commented 4 years ago

There also seems to be issues with navigation properties.

posting this

{
    "id": 1,
    "title": "foobar",
    "blogId": 1
}

Gives this error:

"The Blog field is required."

Model

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}
gathogojr commented 4 years ago

@snebjorn The OData stack doesn't use JSON.NET for serialization. A parameterless constructor is currently required for serialization to work

snebjorn commented 4 years ago

I see. Not sure where I got that idea from. Anyway, support for nullable reference types would be welcome.

What's the reason OData doesn't use the same serializer as AspNetCore? I'm having a real hard time setting up NodaTime and FluentValidation with OData.