Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update a collection inside an entity within a post action in ASP.NET MVC5?

I have created an ASP.NET MVC5 sample project. I created my entities and from that, scaffolded the controllers for CRUD operations. I can only edit the POD members with the scaffolded code. I want to be able to add/remove related entities.

With my current code, when I click save there is no error but no related entities are modified (POD data is modified though). For example, if I wanted to remove all players from the account, they aren't removed. What am I doing wrong?

How can I remove/add related entities and push those changes to the database?

Here is the form: Edit form

Here is the action to update the entity:

public async Task<ActionResult> Edit([Bind(Include = "Account,Account.AccountModelId,Account.Name,Account.CreatedDate,SelectedPlayers")] AccountViewModel_Form vm){
    if (ModelState.IsValid){
        if (vm.SelectedPlayers != null){
            vm.Account.PlayerModels = db.PlayerModels.Where(p => p.AccountModel.AccountModelId == vm.Account.AccountModelId).ToList();
            foreach (var player in vm.Account.PlayerModels){
                player.AccountModel = null;
                db.Entry(player).State = EntityState.Modified;
            }
            vm.Account.PlayerModels.Clear();
            foreach (var player_id in vm.SelectedPlayers){
                var player = db.PlayerModels.Where(p => p.PlayerModelId == player_id).First();
                vm.Account.PlayerModels.Add(player);
                db.Entry(player).State = EntityState.Modified;
            }
        }
        db.Entry(vm.Account).State = EntityState.Modified;
        await db.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    return View(vm);
}

Here are the models:

public class AccountViewModel_Form{
    public AccountModel Account { get; set; }
    public HashSet<Int32> SelectedPlayers { get; set; }
    public virtual List<PlayerModel> PlayersList { get; set; }
}
public class AccountModel{
    public AccountModel(){
        PlayerModels = new HashSet<PlayerModel>();
    }
    public Int32 AccountModelId { get; set; }
    public string Name { get; set; }
    public DateTime CreatedDate { get; set; }
    public virtual ICollection<PlayerModel> PlayerModels { get; set; }
}
public class PlayerModel{
    public Int32 PlayerModelId { get; set; }
    public float Gold { get; set; }
    public string Name { get; set; }
    public virtual AccountModel AccountModel { get; set; }
}

I'm basically lost. I can't find any examples in how to update related data. Could someone point me in the right direction?

I come from Symfony (PHP Framework) background. I thought it would be easier but I have been having problems.

like image 389
jonv1 Avatar asked Oct 31 '22 21:10

jonv1


1 Answers

Basically I was missing the Attach function and that I had to force the load on the collection to make it work.

I found how to attach a non-attached entity here: Model binding in the controller when form is posted - navigation properties are not loaded automatically

When you post the data, the entity is not attached to the context, and when you try to save changes to a complex entity, the context makes a mess.

The code is a little different because I was trying to make it work at home. But it is essentially the same models.

    public ActionResult Edit(AccountEditViewModel vm)
    {
        if (ModelState.IsValid)
        {
            //I was missing these 2 important lines...
            db.Accounts.Attach(vm.Account);
            db.Entry(vm.Account).Collection(a => a.Players).Load();

            if (vm.SelectedPlayers != null)
            {
                foreach (var player in vm.Account.Players.ToList())
                {
                    if (vm.SelectedPlayers.Contains(player.Id) == false)
                    {
                        player.Account = null;
                        vm.Account.Players.Remove(player);
                        db.Entry(player).State = EntityState.Modified;
                        vm.SelectedPlayers.Remove(player.Id);
                    }
                }

                foreach (var player_id in vm.SelectedPlayers)
                {
                    var player = db.Players.Where(p => p.Id == player_id).First();
                    player.Account = vm.Account;
                    vm.Account.Players.Add(player);
                    db.Entry(player).State = EntityState.Modified;
                }
            }else
            {
                vm.Account.Players.Clear();
            }

            db.Entry(vm.Account).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(vm);
    }
like image 154
jonv1 Avatar answered Nov 10 '22 15:11

jonv1