0

I'm trying to have this setup in my asp.net mvc app:

  1. I'm using AspNetUser to handle users.
  2. One user can have many cars
  3. One car can have many repairs

Car model

public class Car
{
    [Key]
    public int CarID { get; set; }

    [Required]
    public virtual ApplicationUser ApplicationUser { get; set; }

    public virtual ICollection<Maintenance> Maintenances { get; set; }

    [Required]
    public string Brand { get; set; }

    // a bunch of other string properties related to a car...
}

Maintenance model

public class Maintenance
{
    public int CarID { get; set; }

    [Key]
    public int MaintenanceID { get; set; }

    public int Mileage { get; set; }

    public DateTime EntryDate { get; set; }

    public DateTime ExitDate { get; set; }

    public decimal Cost { get; set; }

    public virtual Car Automobile { get; }
}

IdentityModels.cs

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<Car> Cars { get; set; }

    // ...
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection", throwIfV1Schema: false)
    {
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Car>()
            .HasRequired(c => c.ApplicationUser)
            .WithMany(t => t.Cars)
            .Map(m => m.MapKey("OwnerID"));
    }

    public DbSet<Car> Cars { get; set; }
    public DbSet<Maintenance> Maintenances { get; set; }
}

I took a look at table definitions, they seems to be OK (OwnerID foreign key is correctly setup), but for some reason this doesn't work when I try to add a new car:

public ActionResult Create(Car car)
{
    if (ModelState.IsValid)
    {
        db.Cars.Add(car);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(car);
}
  1. ModelState.IsValid is always false
  2. car.ApplicationUser is always null

I'm new to asp.net mvc, can someone please tell me what I'm doing wrong here?

TheDude
  • 3,045
  • 4
  • 46
  • 95
  • Do not use data models when editing. Use view models (you view model for `CarVM` will not contain any properties for the user) –  Nov 02 '16 at 07:53
  • Take a look to: http://stackoverflow.com/questions/1352948/how-to-get-all-errors-from-asp-net-mvc-modelstate – Rumpelstinsk Nov 02 '16 at 07:54
  • @StephenMuecke Actually, `Car` does have many properties for the end user, but they're not pertinent to my question, thus I just removed them to lighten the code – TheDude Nov 02 '16 at 08:01
  • @Rumpelstinsk Thanks! `ModelState` is not valid because `Car.ApplicationUser` is `null` – TheDude Nov 02 '16 at 08:02
  • ModelState.IsValid = false means the data you send from the client to your controller aren't valid, it has nothing to do with database. Don't use database models in the view create view models for views instead. You're using the car model in the controller and model binder fails to bind to car model if you don't supply applicationuser and brand in the data you send from client to the server because they're marked as required – lyz Nov 02 '16 at 08:02
  • @lyz Well, that what I'm trying to do: make asp.net fill the `ApplicationUser` with the currently logged in user. The whole point of setting up this relationship between `AspNetUsers` and `Car` was to have this automatically done – TheDude Nov 02 '16 at 08:05
  • 1
    Your not understanding my comment. You should be creating a separate view model for car (say `CarVM`) which contains only the properties your need for editing. Then in the POST method, you initialize a new instance of Car, set its properties based on the view model, set its `OwnerId` property based on the current user and save the data model (and the `[Required]` attribute should be on `OwnerId`, not `ApplicationUser`) –  Nov 02 '16 at 08:17

1 Answers1

0

If the Car.ApplicationUser is always null then I would suggest you look at your View again to see if you have a field for that property, my guess is you don't.

Because you specify the Model as the parameter for the Controller Action:

public ActionResult Create(Car car)

then MVC will attempt to perform Model Binding and in this process the form fields are bound to the Car model using the names of the fields to match up to the model properties.

The only things submitted to the Controller action are those that are within the form.

One option is to include the ApplicationUser field as a hidden field in the form, and within the Controller Action that sends you to the Create View you would create a new Car Model and populate the ApplicationUser there and then pass that into the View.

Another option that @StephenMuecke was getting at was creating a model specifically for the View, and use that ViewModel in the View:

public class CarVM
{
    public ICollection<Maintenance> Maintenances { get; set; }

    [Required]
    public string Brand { get; set; }

    // a bunch of other string properties related to a car...
}

and notice now that the form wouldn't need to worry about the ApplicationUser field in order to get the ModelState to Valid, when the form is submitted it would bind the fields to the properties in the ViewModel, and then in that Post Action you would create a Car model and populate it from the data in the ViewModel that was posted, and grab the current user to populate ApplicationUser, and go from there.

jolyeons
  • 147
  • 6