ArnaudVLK
ArnaudVLK

Reputation: 15

Element from list of object is getting removed when same object is being added to another list

I'm having some issues with a REST API I'm trying to implement using ASP.NET. So basically the issue I'm having is with the following code :

public async Task<IActionResult> AddUserToTutoringSession(int userId, int tutoringSessionId)
{
    var user = await _context.Users.FindAsync(userId);
    var session =  _context.TutoringSessions.Include(session => session.Students).FirstOrDefault(s => s.Id == tutoringSessionId);

    if (user != null && session != null)
    {
        session.Students.Add(user);
        _context.Update(session);
        _context.SaveChanges();
       
        return Ok();
    }
    return BadRequest();
}

This is an HttpPut method where I'm trying to add a user to a tutoring session. A tutoring session (TutoringSession here) has a list of students (List in this case). The issue that I'm having is the following : let's say that I have two TutoringSessions A and B, if a User is in the list of A and I decide to add it to B, it vanishes from A but now exists in B.

If needed here is the code of both the TutoringSession and the User classes.

public class User
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Username { get; set; }
    [EmailAddress]
    [Required]
    public string Mail { get; set; }
    public string Password { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public string Role { get; set; }
}
public class TutoringSession
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public int RelatedCourseId { get; set; }
    public String Subject { get; set; }
    public DateTime? StartTime { get; set; }
    public DateTime? EndTime { get; set; }
    
    public List<User> Students{ get;set; } 
    public User? Tutor { get; set; }
    public bool IsTutored { get; set; }
}

I've tried to modify it directly using this :

 _context.TutoringSessions.Include(session => session.Students).FirstOrDefault(s => s.Id == tutoringSessionId).Students.Add(user)

But as soon as I call SaveChanges(), it does it again.

I'm sure this is something really silly but it's been almost a day of try/fail and research and I can't find where my error lies, if you guys have any ideas, please let me know!

Edit :

My EF Core version is the 7.0.0

And for the tables here are the SQL scripts that Visual Studio made :

User :

CREATE TABLE [dbo].[User] (
    [Id]                INT            IDENTITY (1, 1) NOT NULL,
    [Username]          NVARCHAR (MAX) NOT NULL,
    [Mail]              NVARCHAR (MAX) NOT NULL,
    [Password]          NVARCHAR (MAX) NOT NULL,
    [Name]              NVARCHAR (MAX) NOT NULL,
    [Surname]           NVARCHAR (MAX) NOT NULL,
    [Role]              NVARCHAR (MAX) NOT NULL,
    [TutoringSessionId] INT            NULL,
    CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_User_TutoringSessions_TutoringSessionId] FOREIGN KEY ([TutoringSessionId]) REFERENCES [dbo].[TutoringSessions] ([Id])
);


GO
CREATE NONCLUSTERED INDEX [IX_User_TutoringSessionId]
    ON [dbo].[User]([TutoringSessionId] ASC);

TutoringSessions :

CREATE TABLE [dbo].[TutoringSessions] (
    [Id]              INT            IDENTITY (1, 1) NOT NULL,
    [relatedCourseId] INT            NOT NULL,
    [Subject]         NVARCHAR (MAX) NOT NULL,
    [StartTime]       DATETIME2 (7)  NULL,
    [EndTime]         DATETIME2 (7)  NULL,
    [TutorId]         INT            NULL,
    [IsTutored]       BIT            NOT NULL,
    CONSTRAINT [PK_TutoringSessions] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_TutoringSessions_User_TutorId] FOREIGN KEY ([TutorId]) REFERENCES [dbo].[User] ([Id])
);


GO
CREATE NONCLUSTERED INDEX [IX_TutoringSessions_TutorId]
    ON [dbo].[TutoringSessions]([TutorId] ASC);

If you guys need anything more let me know ! :)

Upvotes: 0

Views: 50

Answers (1)

vernou
vernou

Reputation: 7590

The problem is the user can reference only one tutoring session, because in DB schema the relation between User and TutoringSessions is one to many. That means a tutoring sessions can have many user and a user can have one tutoring session.

In your case, you want a relation many to many, that to say a tutoring sessions can have many user and a user can have many tutoring session. To do this, you need to introduce a intermediate table like :

CREATE TABLE [dbo].[Attendee] (
    [TutoringSessionId]     INT            NOT NULL,
    [UserId]                INT            NOT NULL,
    CONSTRAINT [FK_Attendee_TutoringSession] FOREIGN KEY ([TutoringSessionId]) REFERENCES [dbo].[TutoringSession] ([Id]),
    CONSTRAINT [FK_Attendee_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
)

So when a user is register in a session, it's just a new row in the table Attendee and the precedent attendees are keep.


With EF Core 6, you can declare a many to many relation in many ways, but as there are several relation between user and tutoring session, only the fluent way will work :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<TutoringSession>()
        .HasMany(t => t.Users)
        .WithMany();
}

So EF Core will understand this relation is many to many and will generate the intermediate table on migration.

Upvotes: 1

Related Questions