Reputation: 2708
What is the proper way to POST multiple related entities and check required properties?
I have two entities Product
and Package
. A Product
can have many Packages
. A Package
cannot exist without a related Product
.
When I POST a new Product
with a collection of multiple Packages
, the ModelState
says the model is invalid because the Packages
do not have their required productId
foreign key set. This causes my API to return a BAD REQUEST
status.
It's my understanding that Entity Framework supports this. I would expect the ModelState
to detect that the Package productId
FK will be set automatically and thus wouldn't be invalid.
If I remove the block of code that checks the ModelState
validity Entity Framework behaves as expected and the Product
and Packages
are created.
POST /api/v1/Products
BODY
{
"name": "Coca-Cola",
"packages": [
{
"count": 6,
"quantity": 12,
"quantityUnit": "oz"
}
]
}
Here is my controller function:
[HttpPost]
[ODataRoute("")]
[EnableQuery]
public virtual async Task<IActionResult> PostEntity([FromBody] Product entityToCreate)
{
//Removing this block will cause the request to succeed
// but will also allow invalid requests to get to the database
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Packages.Add(entityToCreate);
await _context.SaveChangesAsync();
return Created("DefaultApi", entityToCreate);
}
And here are my models:
public class Product: ModelBase
{
[Required]
public string name { get; set; }
public List<Package> packages { get; set; }
}
public class Package: ModelBase
{
//some required props...
[Required]
[ForeignKey("product")]
public Guid? productId { get; set; }
public Product product { get; set; }
}
Some things I've investigated:
Dropping the [Required]
attribute on packageId
and manually editing the db migration to include the constraint. This causes an error to be thrown later in the stack by the database. I'd rather fail before getting to the database
Writing my own validation attributes. This seems unnecessary given that the EF documentation claims to support this
Possibly using OData Batch Requests
Upvotes: 1
Views: 862
Reputation: 2818
I typically create a very specific "model" class (not an entity) to represent the payload of an HTTP Post (Put, Patch, etc.). In this case you'd want a set of models. Then I have validation rules specific this set of models. Some call these models, DTOs, ViewModels, etc. The important thing is that they are not EF entities.
After verifying their validity, you will have to map these models into the appropriate entities so they can be persisted with EF. This mapping is the unfortunate tax of decoupling your entities from your exposed models. You can do this manually or use a library like automapper.
There are many advantages to this strategy:
Upvotes: 1