0

How to mutate a one-to-many relationship using EF Core and Dto's?

I'm trying to edit a list of trainees by adding / removing a user using DTO's and EF-core. So far adding a user to the list persists but not the removal. What am I missing?

Thank you so much for the help!

This is the code:

Domain:

public class Session : Entity
{
    #region FIELDS
    private string title;
    private string description;
    private DateTime startsAt;
    private DateTime endsAt;
    private List<User> trainees = new();
    #endregion

    #region PROPERTIES
    public User Coach { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public DateTime? CreatedOn { get; set; }
    public Workout Workout { get; set; }

    public DateTime StartsAt 
    {
        get { return startsAt;  }
        set { startsAt = Guard.Against.Null(value, nameof(startsAt)); } 
    }
    public DateTime EndsAt
    {
        get { return endsAt; }
        set { endsAt = Guard.Against.Null(value, nameof(endsAt)); }
    }

    public string Title 
    {
        get { return title; }
        set { title = Guard.Against.NullOrWhiteSpace(value, nameof(title)); }
    }

    public string Description 
    {
        get { return description; }
        set { description = Guard.Against.NullOrWhiteSpace(value, nameof(description)); }
    }

    public IReadOnlyCollection<User> Trainees => trainees.AsReadOnly();
    #endregion

    #region CONSTRUCTORS
    private Session() { }

    public Session(string title, string description, DateTime startsAt, DateTime endsAt, User coach,Workout workout)
    {
        Title = title;
        Description = description;
        StartsAt = startsAt;
        EndsAt = endsAt;
        Coach = Guard.Against.Null(coach);
        Workout = Guard.Against.Null(workout);
    }
    #endregion

    #region METHODS
    public void AddReservation(User user)
    {
        if (trainees.Count >= 6) throw new ArgumentException("Session is fully booked");
        trainees.Add(user);
        user.AddUserWorkout(Workout);
    }
    public void RemoveReservation(User user)
    {
        trainees.Remove(user);
        user.RemoveUserWorkout(Workout);
    }
    #endregion
}

DTO's:

public static class SessionDto
{
    public class Index
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public DateTime StartsAt { get; set; }
        public DateTime EndsAt { get; set; }
        public string Description { get; set; }
        public WorkoutDto.Index Workout { get; set; }
        public UserDto.Index Coach { get; set; }
    }

    public class Detail : Index
    {
        public List<UserDto.Index>? Trainees { get; set; }
    }
           public class MutateTrainees
        {
            public int TraineeId { get; set; }
            public class Validator : AbstractValidator<Mutate>
            {
                public Validator()
                {

                }
            }

        }
}

Services:

    public async Task<SessionResponse.MutateTrainee> AddTrainee(SessionRequest.MutateTrainee request)
    {
        SessionResponse.MutateTrainee res = new();
        var session = await GetSessionById(request.SessionId).SingleOrDefaultAsync();
        var user = await GetUserById(request.MutateTrainees.TraineeId).SingleOrDefaultAsync();

        Guard.Against.Null(session, nameof(session));
        Guard.Against.Null(user, nameof(user));
        session.AddReservation(user);

        DbCtx.Entry(user).State = EntityState.Modified;
        DbCtx.Entry(session).State = EntityState.Modified;

        await DbCtx.SaveChangesAsync();

        res.SessionId = session.Id;

        return res;
    }

    public async Task<SessionResponse.MutateTrainee> RemoveTrainee(SessionRequest.MutateTrainee request)
    {
        SessionResponse.MutateTrainee res = new();
        var session = await GetSessionById(request.SessionId).SingleOrDefaultAsync();
        var user = await GetUserById(request.MutateTrainees.TraineeId).SingleOrDefaultAsync();

        Guard.Against.Null(session, nameof(session));
        Guard.Against.Null(user, nameof(user));
        session.RemoveReservation(user);

        DbCtx.Entry(session).State = EntityState.Modified;
        DbCtx.Entry(user).State = EntityState.Modified;

        await DbCtx.SaveChangesAsync();

        res.SessionId = session.Id;

        return res;
    }
Dama
  • 1
  • 2
  • I didn't understand, where you say "//TODO: mutate Trainees list", you want to set the session trainees equal to the model.Trainees which is of type List? – spyros__ Nov 19 '22 at 15:43
  • You don't have much exception/error handling in your code, what if `SaveChangesAsync` fails (exception or nothing modified)? Do you catch any exceptions? I don't really understand why you need to modify the EF element State of `user` and `session`, if you modified those objects in `RemoveReservation` and `AddReservation`, everything necessary should be at the right state. – T.Trassoudaine Nov 21 '22 at 16:21

1 Answers1

0

This is how I solved it: Session Service

 public async Task<SessionResponse.MutateTrainee> AddTrainee(SessionRequest.MutateTrainee request)
    {
        SessionResponse.MutateTrainee res = new();
        var session = await DbCtx.Sessions
                          .Include(s => s.Trainees)
                          .FirstOrDefaultAsync(x => x.Id == request.SessionId);
        var user = await GetUserById(request.MutateTrainees.TraineeId).FirstOrDefaultAsync();

        Guard.Against.Null(session, nameof(session));
        Guard.Against.Null(user, nameof(user));

        session.AddReservation(user);

        await DbCtx.SaveChangesAsync();

        res.SessionId = session.Id;

        return res;
    }

    public async Task<SessionResponse.MutateTrainee> RemoveTrainee(SessionRequest.MutateTrainee request)
    {
        SessionResponse.MutateTrainee res = new();
        var session = await DbCtx.Sessions
                          .Include(s => s.Trainees)
                          .FirstOrDefaultAsync(x => x.Id == request.SessionId);

        var user = await GetUserById(request.MutateTrainees.TraineeId).FirstOrDefaultAsync();

        Guard.Against.Null(session, nameof(session));
        Guard.Against.Null(user, nameof(user));

        session.RemoveReservation(user);

        await DbCtx.SaveChangesAsync();

        res.SessionId = session.Id;

        return res;
    }

Dto:

public class MutateTrainees
        {
            public int TraineeId { get; set; }
            public class Validator : AbstractValidator<Mutate>
            {
                public Validator()
                {

                }
            }

        }
Dama
  • 1
  • 2