Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make many to many relationship on same domain model #34224

Closed
GMCadiom opened this issue Jul 14, 2024 · 2 comments
Closed

Make many to many relationship on same domain model #34224

GMCadiom opened this issue Jul 14, 2024 · 2 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@GMCadiom
Copy link

GMCadiom commented Jul 14, 2024

i have those domain models

public class Employee
{
    public int Id { get; set; }
    public string? EmpName { get; set; }
    public virtual ICollection<EmpManager> EmpManagerEmps { get; set; } = new List<EmpManager>();
    public virtual ICollection<EmpManager> EmpManagerMangers { get; set; } = new List<EmpManager>();
}
public class EmpManager
{
    public int Id { get; set; }
    public int EmpId { get; set; }
    public int MangerId { get; set; }
    public virtual Employee Emp { get; set; } = null!;
    public virtual Employee Manger { get; set; } = null!;
}

and the dbcontext is :

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<EmpManager>(entity =>
        {
            entity.HasKey(e => new { e.Id, e.EmpId, e.MangerId }).HasName("PK_emp_manager");

            entity.ToTable("EmpManager");

            entity.Property(e => e.Id).ValueGeneratedOnAdd();

            entity.HasOne(d => d.Emp).WithMany(p => p.EmpManagerEmps)
                .HasForeignKey(d => d.EmpId)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("FK_emp_manager_employees");

            entity.HasOne(d => d.Manger).WithMany(p => p.EmpManagerMangers)
                .HasForeignKey(d => d.MangerId)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("FK_emp_manager_employees1");
        });

        modelBuilder.Entity<Employee>(entity =>
        {
            entity.HasKey(e => e.Id).HasName("PK_employees");

            entity.ToTable("Employee");

            entity.Property(e => e.Id).ValueGeneratedNever();
            entity.Property(e => e.EmpName)
                .HasMaxLength(10)
                .IsFixedLength();
        });

        base.OnModelCreating(modelBuilder);
    }

My test code is :

    internal class Program
    {
        static void Main(string[] args)
        {
            using (TestContext db = new TestContext())
            {
                Employee employee1 = new Employee()
                {
                    Id = 1,
                    EmpName = "emp 1",
                };
                Employee employee2 = new Employee()
                {
                    Id = 2,
                    EmpName = "emp 1",
                };
                Employee employee3 = new Employee()
                {
                    Id = 3,
                    EmpName = "emp 1",
                };
                Employee employee4 = new Employee()
                {
                    Id = 4,
                    EmpName = "emp 1",
                };

                db.Add(employee1);
                db.Add(employee2);
                db.Add(employee3);
                db.Add(employee4);

                db.SaveChanges();

                var id = 2;

                var emp = db.Employees.Include(x => x.EmpManagerMangers).Include(x => x.EmpManagerEmps).FirstOrDefault(x => x.Id == id);

                emp.EmpManagerMangers = new List<EmpManager>()
                {
                     new EmpManager() { MangerId = 4 }
                };

                emp.EmpManagerEmps = emp.EmpManagerMangers;

                db.SaveChanges();
            }
        }
    }

On this example, I just want to make for each employee multiple managers and each manager multiple employees.
but after call SaveChanges() method I found on database that the result is :

The EmpId and MangerId equals the current employee model,
if I am on emp with id = 1 and set for him 2 managers {3,4}
the result became the 2 managers be {1,1} (the current emp id)

in the attachment the test project that i test this issue,

ConsoleApp1.zip

Include provider and version information

EF Core version:
Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer)
Target framework: (e.g. .NET 8.0)
Operating system:
IDE: (e.g. Visual Studio 2022 17.9.3)

@GMCadiom GMCadiom changed the title Make many to many Relashenship on same domain model Make many to many relationship on same domain model Jul 14, 2024
@ajcvickers
Copy link
Member

@GMCadiom There are a couple of things going on here. First, you are re-using the same collection instance for both the EmpManagerMangers and EmpManagerEmps navigations:

emp.EmpManagerMangers = new List<EmpManager>()
{
    new EmpManager() { MangerId = 4 }
};

emp.EmpManagerEmps = emp.EmpManagerMangers;

Each navigation must have its own collection so it can be updated independently to other navigations.

Second, the foreign key property associated with EmpManagerMangers navigation is MangerId. This means setting either of these properties (the navigation or the foreign key) can be used to set the relationship. The instance you're using has PK 2, but then you're using 4 as the FK.

If you're instead trying to set the FK on the other side of the relationship, then that FK property is EmpId. So setting this FK results in employee 2 being related to employee 4:

emp.EmpManagerMangers = new List<EmpManager>()
{
     new EmpManager() { EmpId = 4 }
};

// No need to set the inverse navigation
// emp.EmpManagerEmps = emp.EmpManagerMangers;

db.SaveChanges();

(You may want to rename your FK properties.)

Results in the following entities before SaveChanges:

Employee {Id: 1} Unchanged
    Id: 1 PK
    EmpName: 'emp 1'
  EmpManagerEmps: []
  EmpManagerMangers: []
Employee {Id: 2} Unchanged
    Id: 2 PK
    EmpName: 'emp 1'
  EmpManagerEmps: []
  EmpManagerMangers: [{Id: -2147482647, EmpId: 4, MangerId: 2}]
Employee {Id: 3} Unchanged
    Id: 3 PK
    EmpName: 'emp 1'
  EmpManagerEmps: []
  EmpManagerMangers: []
Employee {Id: 4} Unchanged
    Id: 4 PK
    EmpName: 'emp 1'
  EmpManagerEmps: [{Id: -2147482647, EmpId: 4, MangerId: 2}]
  EmpManagerMangers: []
EmpManager {Id: -2147482647, EmpId: 4, MangerId: 2} Added
    Id: -2147482647 PK Temporary
    EmpId: 4 PK FK
    MangerId: 2 PK FK
  Emp: {Id: 4}
  Manger: {Id: 2}

And after SaveChanges:

Employee {Id: 1} Unchanged
    Id: 1 PK
    EmpName: 'emp 1'
  EmpManagerEmps: []
  EmpManagerMangers: []
Employee {Id: 2} Unchanged
    Id: 2 PK
    EmpName: 'emp 1'
  EmpManagerEmps: []
  EmpManagerMangers: [{Id: 1, EmpId: 4, MangerId: 2}]
Employee {Id: 3} Unchanged
    Id: 3 PK
    EmpName: 'emp 1'
  EmpManagerEmps: []
  EmpManagerMangers: []
Employee {Id: 4} Unchanged
    Id: 4 PK
    EmpName: 'emp 1'
  EmpManagerEmps: [{Id: 1, EmpId: 4, MangerId: 2}]
  EmpManagerMangers: []
EmpManager {Id: 1, EmpId: 4, MangerId: 2} Unchanged
    Id: 1 PK
    EmpId: 4 PK FK
    MangerId: 2 PK FK
  Emp: {Id: 4}
  Manger: {Id: 2}

@ajcvickers ajcvickers self-assigned this Jul 15, 2024
@GMCadiom
Copy link
Author

@ajcvickers
thank you a lot for your clarification and attention, I wish you the best of luck.

@ajcvickers ajcvickers added the closed-no-further-action The issue is closed and no further action is planned. label Jul 17, 2024
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Jul 17, 2024
@ajcvickers ajcvickers removed their assignment Aug 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

2 participants