Search code examples
c#asp.netentity-frameworkrelational-databaseasp.net-identity

Asp.net C# Many-to-Many Relations EF ApplicationUser


I am admittedly on deep water and although I have been looking looking through numerous 'similar' posts, I cant seem to figure this one out.

I am trying to establish a connection with a "Company model" and with the ApplicationUser. The company holds many AU and the AU's can hold many companies. Although when i create a company and select the different users from a selectList (which i checked, does pass the AU-ID.

Warning: I'm but a rookie and I had troubles figuring this much out following guides and what not - so there might be obvious mistakes.

Model Company:

 public Company()
    {
        this.Users = new List<ApplicationUser>();
    }
    public int ID { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Zip { get; set; }
    public string City { get; set; }
    public string Cvr { get; set; }
    public string Telephone { get; set; }
    public string Email { get; set; }
    public string Website { get; set; }
    public string BillingEmail { get; set; }
    [ForeignKey("UserId")]
    public List<string> UserId { get; set; }
    public virtual List<ApplicationUser> Users { get; set; }
}

Model ApplicationUser

  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
        userIdentity.AddClaim(new Claim("FirstName", this.FirstName.ToString()));
        userIdentity.AddClaim(new Claim("LastName", this.LastName.ToString()));
        return userIdentity;
    }
    public ApplicationUser()
    {
        this.Companies = new List<Company>();
    }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public virtual List<Company> Companies { get; set; }
}

Controller

 [HttpPost]
    public async Task<ActionResult> CreateCompany(CreateCompanyViewModel model)
    {

        if (ModelState.IsValid)
        {


            DAL.CompanyContext context = new DAL.CompanyContext();

            // Creating object from recieved form
            DomainModels.Company newCompany = new DomainModels.Company();


            newCompany.Address = model.Address;
            newCompany.BillingEmail = model.BillingEmail;
            newCompany.City = model.City;
            newCompany.Cvr = model.Cvr;
            newCompany.Email = model.Email;
            newCompany.Name = model.Name;
            newCompany.Telephone = model.Telephone;
            newCompany.Website = model.Website;
            newCompany.Zip = model.Zip;
            newCompany.UserId = model.SelectedApplicationUserId;
            // adding and saving
            context.Companies.Add(newCompany);
            await context.SaveChangesAsync();

            return RedirectToAction("Index");
        }
        return View(model);

    }

DbContext

 public DbSet<Company> Companies { get; set; }
    protected override void OnModelCreating(DbModelBuilder mb) 
    {
        base.OnModelCreating(mb);
        mb.Entity<Company>() 
            .HasMany(c => c.Users)
            .WithMany(d =>d.Companies)
            .Map(m=>
            {
            m.MapLeftKey("Company_ID");
            m.MapRightKey("ApplicationUser_Id");
            m.ToTable("CompanyApplicationUsers");
            });
    }

    }

The context I am really confused about. Searching google, I find a lot of posts indicating i need to create an OnModelCreate, although I thought EF would automatically do this - Mind you, it has created a Table automatically, containing the "CompanyApplicationUsers" table (which is why i use those informations).

The company is created each time, but nothing is posted to the relational table.

I got a feeling I'm messing around here - I hope i'll learn! :)

Thanks for any help.

UPDATE 1:

I changed the model and instead point to the ApplicationUser (Users) as the foreign key. I loop through the list of ID's I'm receiving from the view and create a user and add these to the newCompany List..

However while I'm unsure this is correct way, I'm also receiving a validationError "Validation failed for one or more entities" on the Context.savechanges

The errors here are related to the User I create to fill in the List - They require some additional information such as username. Note, I only want the relation, not creating an actual user.

       [HttpPost]
    public async Task<ActionResult> CreateCompany(CreateCompanyViewModel model)
    {

        if (ModelState.IsValid)
        {


            DAL.CompanyContext context = new DAL.CompanyContext();

            // Creating object from recieved form
            DomainModels.Company newCompany = new DomainModels.Company();


            newCompany.Address = model.Address;
            newCompany.BillingEmail = model.BillingEmail;
            newCompany.City = model.City;
            newCompany.Cvr = model.Cvr;
            newCompany.Email = model.Email;
            newCompany.Name = model.Name;
            newCompany.Telephone = model.Telephone;
            newCompany.Website = model.Website;
            newCompany.Zip = model.Zip;
            foreach (string i in model.SelectedApplicationUserId)
            {
                ApplicationUser user = new ApplicationUser();
                user.Id = i;
                newCompany.Users.Add(user);
            }
            //newCompany.Users = model.SelectedApplicationUserId;
            // adding and saving
            context.Companies.Add(newCompany);
            await context.SaveChangesAsync();

            return RedirectToAction("Index");
        }
        return View(model);

    }

Update 2

For now Ill make due with the 1 to many relationship, which i can make work. In this case, i simply update the ApplicationUser with the CompanyID for each associated user. I'd still like to know how to create the many to many, perhaps some of what im doing here, is needed there aswell. But i cant figure it out.

    //newCompany.Users = model.SelectedApplicationUserId;
            // adding and saving

            context.Companies.Add(newCompany);
            try
            {
               int response = await context.SaveChangesAsync();
            }
            catch(Exception exception)
            {

            }

            foreach (string i in model.SelectedApplicationUserId)
            {
                var user = await UserManager.FindByIdAsync(i);
                user.CompanyId = newCompany.ID;
                IdentityResult result = await UserManager.UpdateAsync(user);
            }
            try
            {

            }
            catch (Exception exception)
            {
                throw;
            }

            return RedirectToAction("Index");
        }
        return View(model);

Solution

  • You should be able to do something similar to this with properly configured classes:

    var userList = new List<ApplicationUser>();
    foreach (string i in model.SelectedApplicationUserIds)
    {
        userList.Add(new ApplicationUser {Id = i});
    }
    
    var newCompany = new Company
    {
        Address = model.Address;
        BillingEmail = model.BillingEmail;
        City = model.City;
        // etc. 
        Users = userList
    };
    
    int response = await context.SaveChangesAsync();
    

    EF will make the linkages automatically. Note you don't need to use a UserManager to retrieve users -- it's available from context.Users.