0

So I'm in the middle of a project where I need to have a Many-to-Many relationship between Teams and Members.

One team can have many users (from aspnetusers) and a user can have 0 or more teams.

But at the moment, One team can have many users, but one user can only have 1 team, whenever I try to add a user on a new team, that user is removed from team he's already in.

I've reached this point thanks to http://cpratt.co/associating-related-items-in-a-collection-via-a-listbox/

My Team Model:

public class EquipaModel
    {
        [Key]
        public int EquipaID { get; set; }
        [Required]
        [Display (Name="Nome")]
        public string EquipaNome { get; set; }
        [Required]
        [Display (Name="Descrição")]
        public string EquipaDescricao { get; set; }
        [Display(Name = "Team Manager")]
        public string TeamManagerId { get; set; }
        public virtual ApplicationUser TeamManager { get; set; }

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

My extended user model

public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity>
            GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            var userIdentity = await manager
                .CreateIdentityAsync(this,
                    DefaultAuthenticationTypes.ApplicationCookie);
            return userIdentity;
        }

        public virtual Utilizador Utilizador { get; set; }

    }

    [Table("Utilizadores")]
    public class Utilizador
    {
        public int Id { get; set; }
        public string PrimeiroNome { get; set; }
        public string Apelido { get; set; }
        public int Pontuacao { get; set; }
        public int PaisID { get; set; }
        public virtual PaisModel Pais { get; set; }
        //public IEnumerable<LinguasProgModel> Linguagens { get; set; }

    }

My Team ViewModel

public class EquipasViewModel
    {
        [Required]
        public string EquipaNome { get; set; }
        public string EquipaDescricao { get; set; }
        public string TeamManagerId { get; set; }
        public virtual ApplicationUser TeamManager { get; set; }
        [Required]
        public List<string> MembrosID { get; set; }
        public IEnumerable<SelectListItem> MembrosEscolhidos { get; set; }
    }

My Create on EquipaController (TeamController)

public ActionResult Create()
        {
            ViewBag.TeamManagerId = new SelectList(db.Users, "Id", "Email");

            var model = new EquipasViewModel();

            PopulateMembrosEscolhidos(model);
            return View(model);
        }

        [HttpPost]
        public ActionResult Create(EquipasViewModel model)
        {
            if (ModelState.IsValid)
            {

                var equipa = new EquipaModel
                {
                    EquipaNome = model.EquipaNome,
                    EquipaDescricao = model.EquipaDescricao,
                    TeamManagerId = model.TeamManagerId,
                    Membros = db.Users.Where(m => model.MembrosID.Contains(m.Id)).ToList()
                };

                db.Equipas.Add(equipa);
                db.SaveChanges();

                return RedirectToAction("Index");
            }

            ViewBag.TeamManagerId = new SelectList(db.Users, "Id", "Email", model.TeamManagerId);

            PopulateMembrosEscolhidos(model);
            return View(model);
        }

and finally the Edit on my team controller

public ActionResult Edit(string id)
        {
            ViewBag.TeamManagerId = new SelectList(db.Users, "Id", "Email");

            var equipa = db.Equipas.FirstOrDefault(e => e.EquipaNome == id);
            if (equipa == null)
            {
                return new HttpNotFoundResult();
            }

            var model = new EquipasViewModel
            {
                EquipaNome = equipa.EquipaNome,
                EquipaDescricao = equipa.EquipaDescricao,
                MembrosID = equipa.Membros.Select(m => m.Id).ToList()
            };

            PopulateMembrosEscolhidos(model);
            return View(model);
        }

        [HttpPost]
        public ActionResult Edit(string id, EquipasViewModel model)
        {
            var equipa = db.Equipas.FirstOrDefault(e => e.EquipaNome == id);
            if (equipa == null)
            {
                return new HttpNotFoundResult();
            }

            if (ModelState.IsValid)
            {
                equipa.EquipaNome = model.EquipaNome;
                equipa.EquipaDescricao = model.EquipaDescricao;


                equipa.Membros.Where(m => !model.MembrosID.Contains(m.Id))
                    .ToList()
                    .ForEach(m => equipa.Membros.Remove(m));

                var MembrosNaEquipa = equipa.Membros.Select(m => m.Id);
                var NovosMembros = model.MembrosID.Except(MembrosNaEquipa);
                db.Users.Where(m => NovosMembros.Contains(m.Id))
                    .ToList()
                    .ForEach(m => equipa.Membros.Add(m));

                db.Entry(equipa).State = EntityState.Modified;
                db.SaveChanges();

                return RedirectToAction("Index");
            }

            PopulateMembrosEscolhidos(model);
            return View(model);
        }

At some point, I'm going to remove the selectlist and replace it for a textbox where the user inputs user names to add to the memberlist of the team - but at the moment i'm just trying to figure out how to save the many-to-many relationship.

Edit

I had that feeling that it was something simple, but just couldn't get there. I started using that solution, but came across an error that to be honest, I've never seen before.

Multiplicity constraint violated. The role 'ApplicationUser_Equipas_Source' of the relationship 'Codings.Models.ApplicationUser_Equipas' has multiplicity 1 or 0..1.

It happens on line "db.Equipas.Add(equipa);" of Create.

It's probably my mistake, since I tried to add a team to the users simply by

var MembrosEscolhidos = db.Users.Where(m => model.MembrosID.Contains(m.Id)).ToList();

foreach (var item in MembrosEscolhidos)
                {
                    item.Equipas.Add(equipa);
                }
Ruben S
  • 101
  • 8
  • You see how you reference Users in your Teams Model e.g. "public virtual ICollection Membros { get; set; }"... why not do the same in in your users model but reference the Teams model via: "public virtual ICollection Teams { get; set; }" or is there something I am missing? – Louie Bacaj Feb 12 '15 at 17:04

1 Answers1

0

I think one easy fix would be to reference your EquipaModel as a collection in your users table.

e.g.

public virtual ICollection<EquipaModel> Equipas { get; set; }

like you reference your users in your EquipaModel

e.g.

public class EquipaModel
{
   ...
   public virtual ICollection<ApplicationUser> Membros { get; set; }
}

Then your users can have many "Equipas" or teams.

Louie Bacaj
  • 1,417
  • 13
  • 18
  • That makes sense of course, not sure why I haven't thought about that, although, like I said on my edit, came across another problem :| – Ruben S Feb 12 '15 at 17:34
  • Ok so that is a different problem than the one you originally had... try this for the problem you are having now http://stackoverflow.com/questions/20034003/multiplicity-constraint-violated-entity-framework-5... – Louie Bacaj Feb 12 '15 at 17:46