Reputation: 37
Post updated with solution.
Let's say i have two simple model classes to explain my problem. Each model as navigation properties for DB relationships. Everything it's ok with migrations creation and update-database, also with postman for GET endpoints. The problem... i can POST a User, but can't POST a Car record. I followed the Code First from Microsoft tutorial with Fluent API method, but i also tried with Data Annotations, but same result (between some other tests i also tried with 'virtual' keyword). A brief search pointed me to nested Object/Array upon doing the POST record to DB, but also no success, same error! This is a little example from my project with more than 6 model classes, but with this two i think i can explain my problem. This is for one-to-many relationship.
public class User
{
public int Id { get; set; }
public string Age { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string Country { get; set; }
// navigation property
public List<Car> Cars { get; set; }
}
And Car model:
public class Car
{
public int Id { get; set; }
public int Year { get; set; }
public string Plate { get; set; }
// navigation property
public int UserId { get; set; }
public User User { get; set; }
}
And here's the DbContext:
public MyPersonalProjectContext(DbContextOptions<MyPersonalProjectContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// setting database relationships with constraints and cascade delete
// one-to-many for User -> Car tables
modelBuilder.Entity<Car>()
.HasOne(u => u.User)
.WithMany(c => c.Cars)
.HasForeignKey(u => u.UserId)
.HasConstraintName("FK_User_Cars")
.OnDelete(DeleteBehavior.Cascade);
}
I POST this example record in api/cars endpoint:
{
"year": 2012,
"plate": "YY-11-BB",
"userId": 2
}
Postman gives me:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-36feeb71fcd60b67da499091bf94598a-1b14d3e2c48fc635-00",
"errors": {
"User": [
"The User field is required."
]
}
}
I also tried POSTing an empty array to try to cheat:
{
"year": 2012,
"plate": "YY-11-BB",
"userId": 2,
"user": []
}
CarsController:
public async Task<ActionResult<Car>> PostCar(Car car)
{
if (_context.Cars == null)
{
return Problem("Entity set 'MyPersonalProjectContext.Cars' is null.");
}
_context.Cars.Add(car);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCar", new { id = car.Id }, car);
}
[SOLUTION]: After some digging and try and error, i came up with the solution, at least for my case. I will post here what i've achieved so that many others with the same problem can solve it also!
Create a ModelDTO (for one who don't know what is, here's a good explanation from this 12 years old topic What is a Data Transfer Object (DTO)?)
public class CreateCarDTO
{
public int Id { get; set; }
public int Year { get; set; }
public string Plate { get; set; }
public int UserId { get; set; }
}
Then i've made a little changes in my controller POST method:
[HttpPost]
public async Task<ActionResult<Car>> PostCar(CreateCarDTO request)
{
var user = await _context.Users.FindAsync(request.UserId);
if (user == null)
{
return NotFound();
}
var newCar = new Car
{
Year = request.Year,
Plate = request.Plate,
User = user
};
_context.Cars.Add(newCar);
await _context.SaveChangesAsync();
return await GetCar(newCar.UserId);
}
And finally for JSON exceptions, i added [JsonIgnore] in Model:
public class Car
{
public int Id { get; set; }
public int Year { get; set; }
public string Plate { get; set; }
// navigation property for parent entity and foreign key
// one-to-many car -> user
public int UserId { get; set; }
[JsonIgnore]
public User User { get; set; }
}
Hope this solution help others :-).
Upvotes: 2
Views: 2459
Reputation: 492
First of all If you want to save Cars without User you should Cars entity as below.
public class Car
{
public int Id { get; set; }
public int Year { get; set; }
public string Plate { get; set; }
// navigation property make nullable
public int? UserId { get; set; }
public User User { get; set; }
}
If you want to save with user in this case that User must exist in database. For example in your example User must exists in database with id=2
Upvotes: 2