Search code examples
c#sql-serverentity-framework-coreasp.net-core-mvcmany-to-many

Configure many-to-many relationship in ASP.NET Core MVC and Entity Framework against


I am trying to configure the relationship many-to-many between two tables, Employee and Project.

One Employee can participate in many projects, and one project can have many Employees working on it. So I created two model classes Employee and Project, and I added the table Employee_Project.

These are my three model classes:

namespace WebApp2.Models
{
    public class Employee
    {
        [Key]
        public int Emp_Id { get; set; }
        public string Emp_Name { get; set; }
        public string Emp_Email { get; set; }
        public string Emp_Mobile { get; set; }

        public virtual ICollection<Employee_Project> Employee_Projects { get; set; }
    }

    public class Project
    {
        [Key]
        public int Proj_Id { get; set; }
        public string Proj_Name { get; set; }
        public string Project_Details { get; set; }
        public virtual ICollection<Employee_Project> Employee_Projects { get; set; }
    }

    public class Employee_Project
    {
        [Key]
        [Column(Order =1)]
        public int Emp_Id { get; set; }
        [Key]
        [Column(Order = 2)]
        public int Proj_Id { get; set; }

        public virtual Employee Employee { get; set; }
        public virtual Project Project { get; set; }
    }
}

I then added this DbContext class:

namespace WebApp2.Data
{
    public class MyDbContext:DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> option):base(option)
        {
        }

        public DbSet<Employee> Employees { get; set; }
        public DbSet<Project> Projects { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Employee_Project>().HasKey(pt => new { pt.Proj_Id, pt.Emp_Id });

            modelBuilder.Entity<Employee_Project>()
                        .HasOne(pt => pt.Employee)
                        .WithMany(pt => pt.Employee_Projects)
                        .HasForeignKey(p => p.Emp_Id);

            modelBuilder.Entity<Employee_Project>()
                        .HasOne(pt => pt.Project)
                        .WithMany(pt => pt.Employee_Projects)
                        .HasForeignKey(p => p.Proj_Id);
        }

        public DbSet<Employee_Project> Employee_Projects { get; set; }
    }
}

I created after that the three controllers

public class ProjectController : Controller
    {
        private readonly MyDbContext _context;

        public ProjectController(MyDbContext context)
        {
            _context = context;
        }
        public IActionResult Index()
        {
            return View(_context.projects.ToList());
        }

        public IActionResult Create()
        {
            return View();
        }
        [HttpPost]
        public IActionResult Create(Project project)
        {
            _context.projects.Add(project);
            _context.SaveChanges();
            return RedirectToAction("Index");
        }
    }




public class EmployeeController : Controller
    {
        private readonly MyDbContext _context;

        public EmployeeController(MyDbContext context)
        {
            _context = context;
        }
        public IActionResult Index()
        {
            return View(_context.Employees.ToList());
        }

        public IActionResult Create()
        {
            return View();
        }
        [HttpPost]
        public IActionResult Create(Employee employee)
        {
             _context.Employees.Add(employee);
            _context.SaveChanges(); 
            return RedirectToAction("Index");
        }
    }





public class Emp_ProjController : Controller
    {
        private readonly MyDbContext _DbContext;

        public Emp_ProjController(MyDbContext DbContext)
        {
            _DbContext = DbContext;
        }
        public IActionResult Index()
        {
            return View(_DbContext.Employee_Projects.ToList());
        }

        
        public IActionResult Create()
        {
            ViewBag.emp=_DbContext.Employees.ToList();
            ViewBag.pro=_DbContext.projects.ToList();
            return View();
        }

        [HttpPost]
        public IActionResult Create(int empid, int [] projIds)
        {
            foreach (var item in projIds)
            {
                Employee_Project emp = new Employee_Project();
                emp.Emp_Id = empid;
                emp.Proj_Id = item;
                _DbContext.Employee_Projects.Add(emp);
                _DbContext.SaveChanges();

            }
            return RedirectToAction("Index");
            
            
        }

    }


After that foreach Controllers I made the view for the method Index and Create

Emp_Proj

//view Index

@model IEnumerable<WebApp2.Models.Employee_Project>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Employee.Emp_Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project.Proj_Name)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Employee.Emp_Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Project.Proj_Name)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
                @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
                @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
            </td>
        </tr>
}
    </tbody>
</table>

//view Create

<h2>Create</h2>

<form method="post">
    <div>
        <label>Employee Name</label>
        @Html.DropDownList("empid", new SelectList(ViewBag.emp, "Emp_Id","Emp_Email"),"Select Employee")
    </div>
    <div>
        <label>Select Project</label>
       @* @Html.DropDownList("proid", new SelectList(ViewBag.pro, "Proj_Id","Proj_Name"),"Select Project")*@

        <ul>
            @foreach(var item in ViewBag.pro )
            {
                <li>
                    <input type="checkbox" name="projIds" value="@item.Proj_Id">@item.Proj_Name
                </li>
            }
        </ul>

        <input  type="submit" value="SaveData"/>
        
    </div>
</form>



I don't have problem in the Employee and the project, I found the problem when I want to create a Emp_Proj element enter image description here It always gives me an error like that:

SqlException: Violation of PRIMARY KEY constraint 'PK_Employee_Projects'. Cannot insert duplicate key into object 'dbo.Employee_Projects'. Duplicate key value: (1, 1). The instruction has been terminated.

enter image description here

Can someone please help me to find the problem? Thanks in advance.

I try to find the problem, and I appreciate some assistance.


Solution

  • The error message has show the reason why cause this exception: The database already contains this record, you can't insert duplicate data. You just need to check if it already exists in the database before inserting data, Please refer to this simple demo.

    [HttpPost]
            public IActionResult Create(int empid, int[] projIds)
            {
                foreach (var item in projIds)
                {
                    //check if the database already has this record
                    var empdb = _DbContext.Employee_Projects.Where(x => x.Emp_Id == empid && x.Proj_Id == item).FirstOrDefault();
    
                    if (empdb==null)
                    {
                        Employee_Project emp = new Employee_Project();
                        emp.Emp_Id = empid;
                        emp.Proj_Id = item;
                        _DbContext.Employee_Projects.Add(emp);
                    }
                    
               
                }
                _DbContext.SaveChanges();
                return RedirectToAction("Index");
            }