Search code examples
asp.net-mvcentity-frameworkasp.net-mvc-4many-to-manycode-first

Nothing saved in database and no errors, many to many relation


Have a many to many relationship between users and posts. I want to create new values in the post table and the association table with postID and logged in user's ID. No error message and it looks like I can create a post when I run the application but nothing is saved in any table. I can see when I run debug it goes all the steps, even save to the database. What's wrong?

Post model

public class Post
    {
        public Post()
        {
            this.ApplicationUser = new HashSet<ApplicationUser>();
        }

        public int PostId { get; set; }
        public string Message { get; set; }
        public DateTime MessageDate { get; set; }

        public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }
    }

IdentityModels

 public class ApplicationUser : IdentityUser
    {
       public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

       public ApplicationUser()
       {
           this.Posts = new HashSet<Post>();
       }

       public virtual ICollection<Post> Posts { get; set; }
    }

And my create function to create a new post

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "PostId,Message,MessageDate")] Post post)
    {
        var manager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var userId = User.Identity.GetUserId();
        var user = manager.FindById(userId);

        if (ModelState.IsValid)
        {
            user.Posts.Add(post);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(post);
    }

I have values in all the variables for the user (manager, userId, user) and three tables (Posts, AspNetUsers and ApplicationUserPost)

Update

Error when I try Attach (and solution from CodeNotFound).

Error 1 'System.Data.Entity.IDbSet' does not contain a definition for 'FindById' and no extension method 'FindById' accepting a first argument of type 'System.Data.Entity.IDbSet' could be found (are you missing a using directive or an assembly reference?)

Error 2 'FreePost.Models.ApplicationDbContext' does not contain a definition for 'Attach' and no extension method 'Attach' accepting a first argument of type 'FreePost.Models.ApplicationDbContext' could be found (are you missing a using directive or an assembly reference?)

The first error message is the reason I chose to use userManager.


Solution

  • It isn't working because your db.SaveChanges does nothing. Your context does nothing because it know nothing about the Post instance you're attempting to add.

    To fix that you need to first attach the current instance of ApplicationUser to the context like below:

    var manager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
    var userId = User.Identity.GetUserId();
    var user = manager.FindById(userId);
    
    if (ModelState.IsValid)
    {
        // Line below you tell your DbContext to take care of ApplicationUser instance. 
        db.Users.Attach(user); 
        user.Posts.Add(post);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    

    Side notes: I will refactor your code like this:

    if (ModelState.IsValid)
    {
        // Move your code that query the database here so you query your database only if the model is valid.
        var userId = User.Identity.GetUserId();
    
        // You don't need ApplicationUserManager instance to get the ApplicationUser  instance. 
        // If your Post class have a UserId foreign key property then this line is uselesss.
        // Just set the UserId property of Post class.
        var user = db.Users.Find(userId);
        user.Posts.Add(post);
        db.SaveChanges();
        return RedirectToAction("Index");
    }