2

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 :-).

raOliveira
  • 37
  • 1
  • 7

1 Answers1

2

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

Yusif Karimov
  • 400
  • 5
  • 8
  • My mistake when put code in comment, sorry. Yusif Karimov, also tried with your answer and have same ERROR. Can´t understand what's wrong. When I update-database I create seed data, so DB it's populated. – raOliveira Jun 03 '22 at 08:33
  • I tried to your code which you mentioned in up comment. And it worked. Like below { "year": 2023, "plate": "YY-99-BB", "userId": 1 } I used this request model. public async Task> PostCar(Car car) { try { 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); } catch (Exception ex) { throw; } } – Yusif Karimov Jun 03 '22 at 10:00
  • Did you check User table for Id = 2 ? – Yusif Karimov Jun 03 '22 at 10:04
  • that is weird, could i have some error else where and i can't find it?! here's my seed data that proves my Id = 2 https://raoliveirablob.blob.core.windows.net/newcontainer/seed%20data.png – raOliveira Jun 03 '22 at 11:18
  • I can only made a POST if i nest the object like this.... And this is not acceptable for me. Also, Postman gives me the error for Json larger than the maximum allowed depth of 32, so i have to include JsonSerializerOptions in services to complete POST. { "year": 2012, "plate": "YY-11-BB", "userId": 10, "user": { "name": "asdas", "age": "15", "country": "pt", "city": "asasdf", "cars": [] }} https://raoliveirablob.blob.core.windows.net/newcontainer/json_error.png – raOliveira Jun 03 '22 at 11:42