Ejemplo n.º 1
0
        /// <summary>
        /// Ensures that a question to add or update is in a valid state.
        /// </summary>
        public async Task <bool> ValidateQuestionAsync(
            Question question,
            IModelErrorCollection errors,
            string classroomName)
        {
            var existingQuestion = await _dbContext.Questions
                                   .Where(q => q.QuestionCategory.Classroom.Name == classroomName)
                                   .Where(q => q.Id == question.Id)
                                   .Include(q => q.QuestionCategory)
                                   .SingleOrDefaultAsync();

            if (existingQuestion != null)
            {
                _dbContext.Entry(existingQuestion).State = EntityState.Detached;
            }

            var newQuestionCategory = await _dbContext.QuestionCategories
                                      .Include(qc => qc.Classroom)
                                      .SingleOrDefaultAsync
                                      (
                category => category.Id == question.QuestionCategoryId
                                      );

            if (newQuestionCategory.Classroom.Name != classroomName)
            {
                throw new InvalidOperationException(
                          "Category of question is not in the given classroom.");
            }

            if (existingQuestion?.QuestionCategory?.RandomlySelectedQuestionId != null &&
                question.QuestionCategoryId != existingQuestion?.QuestionCategoryId)
            {
                throw new InvalidOperationException
                      (
                          "The category cannot be changed for a randomly selected question choice."
                      );
            }

            if (existingQuestion != null &&
                existingQuestion?.QuestionCategory?.RandomlySelectedQuestionId == null &&
                newQuestionCategory.RandomlySelectedQuestionId != null)
            {
                throw new InvalidOperationException
                      (
                          "The category cannot be changed from a non-random-choice category "
                          + "to a random choice category."
                      );
            }

            if (await _dbContext.Questions.AnyAsync(
                    q => q.Id != question.Id &&
                    q.Name == question.Name &&
                    q.QuestionCategoryId == question.QuestionCategoryId))
            {
                errors.AddError("Name", "Another question with that name already exists.");
            }

            return(!errors.HasErrors);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Validates that an announcement is correctly configured.
        /// </summary>
        public bool ValidateAnnouncement(
            Classroom classroom,
            Announcement announcement,
            IModelErrorCollection modelErrors)
        {
            var sectionIds = announcement.Sections
                             ?.Select(s => s.SectionId)
                             ?.ToList() ?? new List <int>();

            if (!sectionIds.Any())
            {
                modelErrors.AddError
                (
                    "Sections",
                    "At least one section must be included."
                );

                return(false);
            }

            if (sectionIds.Distinct().Count() != sectionIds.Count)
            {
                modelErrors.AddError
                (
                    "Sections",
                    "Duplicate sections are not permitted."
                );

                return(false);
            }

            if (sectionIds
                .Intersect(classroom.Sections.Select(s => s.Id))
                .Count() != sectionIds.Count)
            {
                modelErrors.AddError
                (
                    "Sections",
                    "Invalid sections selected."
                );

                return(false);
            }

            return(true);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Registers a user.
        /// </summary>
        public async Task <RegisterNewUserResult> RegisterNewStudentAsync(
            string classroomName,
            string sectionName,
            StudentRegistration registration,
            string confirmationUrlBuilder,
            IModelErrorCollection errors)
        {
            var section = _dbContext.Sections
                          .Where(s => s.Classroom.Name == classroomName)
                          .Include(s => s.Classroom)
                          .SingleOrDefault(s => s.Name == sectionName);

            if (section == null)
            {
                return(RegisterNewUserResult.SectionNotFound);
            }

            if (!section.AllowNewRegistrations)
            {
                return(RegisterNewUserResult.SectionNotOpen);
            }

            var user = await GetAndUpdateCurrentUserAsync();

            if (user != null)
            {
                return(RegisterNewUserResult.AlreadyRegistered);
            }

            if (!await _gitHubUserClient.DoesUserExistAsync(registration.GitHubLogin))
            {
                errors.AddError("GitHubLogin", "The GitHub username does not exist.");
                return(RegisterNewUserResult.Failed);
            }

            user = new User()
            {
                UniqueId              = _identityProvider.CurrentIdentity.UniqueId,
                UserName              = _identityProvider.CurrentIdentity.UserName,
                FirstName             = registration.FirstName,
                LastName              = _identityProvider.CurrentIdentity.LastName,
                EmailAddress          = registration.EmailAddress,
                EmailConfirmationCode = GenerateEmailConfirmationCode(),
                EmailAddressConfirmed = false,
                GitHubLogin           = registration.GitHubLogin,
                SuperUser             = false
            };

            var membership = await EnsureSectionMembershipAsync(user, section, SectionRole.Student);

            await EnsureUserInGithubOrgAsync(user, membership.ClassroomMembership);
            await SendUserInvitationMailAsync(user, confirmationUrlBuilder);

            _dbContext.Users.Add(user);
            await _dbContext.SaveChangesAsync();

            return(RegisterNewUserResult.Success);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Updates a checkpoint.
        /// </summary>
        private bool UpdateCheckpoint(Checkpoint checkpoint, IModelErrorCollection modelErrors)
        {
            if (checkpoint.SectionDates != null)
            {
                var sections = checkpoint.SectionDates.Select(d => d.SectionId).ToList();
                if (sections.Distinct().Count() != sections.Count)
                {
                    modelErrors.AddError("SectionDates", "You may only have one due date per section.");
                }
            }

            if (checkpoint.TestClasses != null)
            {
                var testClasses = checkpoint.TestClasses.Select(tc => tc.TestClassId).ToList();
                if (testClasses.Distinct().Count() != testClasses.Count)
                {
                    modelErrors.AddError("TestClasses", "You may only have one entry per test class.");
                }
            }

            if (modelErrors.HasErrors)
            {
                return(false);
            }

            _dbContext.RemoveUnwantedObjects
            (
                _dbContext.CheckpointDates,
                checkpointDates => checkpointDates.Id,
                checkpointDates => checkpointDates.CheckpointId == checkpoint.Id,
                checkpoint.SectionDates
            );

            _dbContext.RemoveUnwantedObjects
            (
                _dbContext.CheckpointTestClasses,
                testClass => testClass.Id,
                testClass => testClass.CheckpointId == checkpoint.Id,
                checkpoint.TestClasses
            );

            return(true);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Called to register the first super-user.
        /// </summary>
        public async Task <RegisterNewUserResult> RegisterFirstSuperUserAsync(
            SuperUserRegistration registration,
            IModelErrorCollection errors)
        {
            if (await AnyRegisteredUsersAsync())
            {
                return(RegisterNewUserResult.AlreadyRegistered);
            }

            if (registration.ActivationToken != _activationToken.Value)
            {
                errors.AddError("ActivationToken", "Incorrect activation token.");
                return(RegisterNewUserResult.Failed);
            }

            if (!await _gitHubUserClient.DoesUserExistAsync(registration.GitHubLogin))
            {
                errors.AddError("GitHubLogin", "The GitHub username does not exist.");
                return(RegisterNewUserResult.Failed);
            }

            User user = new User()
            {
                UniqueId              = _identityProvider.CurrentIdentity.UniqueId,
                UserName              = _identityProvider.CurrentIdentity.UserName,
                FirstName             = registration.FirstName,
                LastName              = registration.LastName,
                EmailAddress          = registration.EmailAddress,
                EmailAddressConfirmed = true,
                GitHubLogin           = registration.GitHubLogin,
                SuperUser             = true
            };

            _dbContext.Users.Add(user);
            await _dbContext.SaveChangesAsync();

            return(RegisterNewUserResult.Success);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Ensures that there are no duplicate section recipients.
        /// </summary>
        private void EnsureNoDuplicateSectionRecipients(
            Section section,
            IModelErrorCollection errors)
        {
            if (section.SectionRecipients != null)
            {
                var cmIds = section.SectionRecipients
                            .Select(d => d.ClassroomMembershipId)
                            .ToList();

                if (cmIds.Distinct().Count() != cmIds.Count)
                {
                    errors.AddError
                    (
                        "SectionRecipients",
                        "Duplicate section recipients are not permitted."
                    );
                }
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Ensures that there are no duplicate section gradebooks.
        /// </summary>
        private void EnsureNoDuplicateSectionGradebooks(
            Section section,
            IModelErrorCollection errors)
        {
            if (section.SectionGradebooks != null)
            {
                var classroomGradebookIds = section.SectionGradebooks
                                            .Select(d => d.ClassroomGradebookId)
                                            .ToList();

                if (classroomGradebookIds.Distinct().Count() != classroomGradebookIds.Count)
                {
                    errors.AddError
                    (
                        "SectionGradebooks",
                        "You may only have one section gradebook per classroom gradebook."
                    );
                }
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Ensures that section recipients are class admins.
        /// </summary>
        private async Task EnsureSectionRecipientsAreClassAdmins(
            Section section,
            IModelErrorCollection errors)
        {
            var classroomMemberships = await _dbContext.ClassroomMemberships
                                       .Where(cm => cm.ClassroomId == section.ClassroomId)
                                       .Where(cm => cm.Role >= ClassroomRole.Admin)
                                       .ToListAsync();

            var cmIds = classroomMemberships.Select(cm => cm.Id).ToHashSet();

            if (section.SectionRecipients != null &&
                section.SectionRecipients.Any(sr => !cmIds.Contains(sr.ClassroomMembershipId)))
            {
                errors.AddError
                (
                    "SectionRecipients",
                    "All section recipients must be class admins."
                );
            }
        }
        /// <summary>
        /// Returns a mock question updater factory.
        /// </summary>
        private Mock <IQuestionUpdaterFactory> GetMockQuestionUpdaterFactory(
            bool isValid)
        {
            var updater = new Mock <IQuestionUpdater>();
            IModelErrorCollection errors = null;

            var updaterFactory = new Mock <IQuestionUpdaterFactory>();

            updaterFactory
            .Setup
            (
                m => m.CreateQuestionUpdater
                (
                    It.IsNotNull <Question>(),
                    It.IsNotNull <IModelErrorCollection>()
                )
            ).Callback <Question, IModelErrorCollection>
            (
                (_unused, modelErrors) => errors = modelErrors
            ).Returns(updater.Object);

            updater
            .Setup(m => m.UpdateQuestionAsync())
            .Callback
            (
                () =>
            {
                if (!isValid)
                {
                    errors.AddError("Error", "ErrorDescription");
                }
            }
            ).Returns(Task.CompletedTask);

            return(updaterFactory);
        }
Ejemplo n.º 10
0
		/// <summary>
		/// Registers a user.
		/// </summary>
		public async Task<RegisterNewUserResult> RegisterNewStudentAsync(
			string classroomName,
			string sectionName,
			StudentRegistration registration,
			string confirmationUrlBuilder,
			IModelErrorCollection errors)
		{
			var section = _dbContext.Sections
				.Where(s => s.Classroom.Name == classroomName)
				.Include(s => s.Classroom)
				.SingleOrDefault(s => s.Name == sectionName);

			if (section == null)
			{
				return RegisterNewUserResult.SectionNotFound;
			}

			if (!section.AllowNewRegistrations)
			{
				return RegisterNewUserResult.SectionNotOpen;
			}

			var user = await GetAndUpdateCurrentUserAsync();
			if (user != null)
			{
				return RegisterNewUserResult.AlreadyRegistered;
			}
			
			if (!await _gitHubUserClient.DoesUserExistAsync(registration.GitHubLogin))
			{
				errors.AddError("GitHubLogin", "The GitHub username does not exist.");
				return RegisterNewUserResult.Failed;
			}

			user = new User()
			{
				UniqueId = _identityProvider.CurrentIdentity.UniqueId,
				UserName = _identityProvider.CurrentIdentity.UserName,
				FirstName = registration.FirstName,
				LastName = _identityProvider.CurrentIdentity.LastName,
				EmailAddress = registration.EmailAddress,
				EmailConfirmationCode = GenerateEmailConfirmationCode(),
				EmailAddressConfirmed = false,
				GitHubLogin = registration.GitHubLogin,
				SuperUser = false
			};

			var membership = await EnsureSectionMembershipAsync(user, section, SectionRole.Student);		
			await EnsureUserInGithubOrgAsync(user, membership.ClassroomMembership);
			await SendUserInvitationMailAsync(user, confirmationUrlBuilder);

			_dbContext.Users.Add(user);
			await _dbContext.SaveChangesAsync();

			return RegisterNewUserResult.Success;
		}
Ejemplo n.º 11
0
		/// <summary>
		/// Called to register the first super-user.
		/// </summary>
		public async Task<RegisterNewUserResult> RegisterFirstSuperUserAsync(
			SuperUserRegistration registration,
			IModelErrorCollection errors)
		{
			if (await AnyRegisteredUsersAsync())
			{
				return RegisterNewUserResult.AlreadyRegistered;
			}

			if (registration.ActivationToken != _activationToken.Value)
			{
				errors.AddError("ActivationToken", "Incorrect activation token.");
				return RegisterNewUserResult.Failed;
			}

			if (!await _gitHubUserClient.DoesUserExistAsync(registration.GitHubLogin))
			{
				errors.AddError("GitHubLogin", "The GitHub username does not exist.");
				return RegisterNewUserResult.Failed;
			}

			User user = new User()
			{
				UniqueId = _identityProvider.CurrentIdentity.UniqueId,
				UserName = _identityProvider.CurrentIdentity.UserName,
				FirstName = registration.FirstName,
				LastName = registration.LastName,
				EmailAddress = registration.EmailAddress,
				EmailAddressConfirmed = true,
				GitHubLogin = registration.GitHubLogin,
				SuperUser = true
			};
			
			_dbContext.Users.Add(user);
			await _dbContext.SaveChangesAsync();

			return RegisterNewUserResult.Success;
		}
Ejemplo n.º 12
0
		/// <summary>
		/// Updates the given user.
		/// </summary>
		public async Task<bool> UpdateUserAsync(
			User user,
			string confirmationUrlBuilder,
			IModelErrorCollection modelErrors)
		{
			var existingUser = await _dbContext.Users
				.Include(u => u.ClassroomMemberships)
					.ThenInclude(cm => cm.Classroom)
				.SingleAsync(u => u.Id == user.Id);

			bool updatedEmail = false;

			if (user.GitHubLogin != existingUser.GitHubLogin)
			{
				if (!await _gitHubUserClient.DoesUserExistAsync(user.GitHubLogin))
				{
					modelErrors.AddError("GitHubLogin", "The GitHub username does not exist.");
					return false;
				}

				if (existingUser.ClassroomMemberships != null)
				{
					foreach (var membership in existingUser.ClassroomMemberships)
					{
						var orgName = membership.Classroom.GitHubOrganization;

						var team = await _gitHubTeamClient.GetTeamAsync
						(
							orgName,
							membership.GitHubTeam
						);

						await _gitHubTeamClient.InviteUserToTeamAsync
						(
							orgName,
							team,
							user.GitHubLogin
						);

						await _gitHubTeamClient.RemoveUserFromTeamAsync
						(
							orgName,
							team,
							existingUser.GitHubLogin
						);

						membership.InGitHubOrganization = false;
					}
				}

				existingUser.GitHubLogin = user.GitHubLogin;
			}

			if (user.EmailAddress != existingUser.EmailAddress)
			{
				existingUser.EmailAddress = user.EmailAddress;
				existingUser.EmailAddressConfirmed = false;
				existingUser.EmailConfirmationCode = GenerateEmailConfirmationCode();

				updatedEmail = true;
			}

			await _dbContext.SaveChangesAsync();

			if (updatedEmail)
			{
				await SendUserInvitationMailAsync(existingUser, confirmationUrlBuilder);
			}

			return true;
		}
Ejemplo n.º 13
0
        /// <summary>
        /// Updates the given user.
        /// </summary>
        public async Task <bool> UpdateUserAsync(
            User user,
            string confirmationUrlBuilder,
            IModelErrorCollection modelErrors)
        {
            var existingUser = await _dbContext.Users
                               .Include(u => u.ClassroomMemberships)
                               .ThenInclude(cm => cm.Classroom)
                               .Include(u => u.AdditionalContacts)
                               .SingleAsync(u => u.Id == user.Id);

            bool updatedEmail = false;

            if (user.GitHubLogin != existingUser.GitHubLogin)
            {
                if (!await _gitHubUserClient.DoesUserExistAsync(user.GitHubLogin))
                {
                    modelErrors.AddError("GitHubLogin", "The GitHub username does not exist.");
                    return(false);
                }

                if (existingUser.ClassroomMemberships != null)
                {
                    foreach (var membership in existingUser.ClassroomMemberships)
                    {
                        var orgName = membership.Classroom.GitHubOrganization;

                        var team = await _gitHubTeamClient.GetTeamAsync
                                   (
                            orgName,
                            membership.GitHubTeam
                                   );

                        await _gitHubTeamClient.InviteUserToTeamAsync
                        (
                            orgName,
                            team,
                            user.GitHubLogin
                        );

                        await _gitHubTeamClient.RemoveUserFromTeamAsync
                        (
                            orgName,
                            team,
                            existingUser.GitHubLogin
                        );

                        membership.InGitHubOrganization = false;
                    }
                }

                existingUser.GitHubLogin = user.GitHubLogin;
            }

            if (user.PublicName != existingUser.PublicName)
            {
                existingUser.PublicName = user.PublicName;
            }

            if (user.EmailAddress != existingUser.EmailAddress)
            {
                existingUser.EmailAddress          = user.EmailAddress;
                existingUser.EmailAddressConfirmed = false;
                existingUser.EmailConfirmationCode = GenerateEmailConfirmationCode();

                updatedEmail = true;
            }

            foreach (var additionalContact in existingUser.AdditionalContacts.ToList())
            {
                var modifiedContact = user.AdditionalContacts
                                      ?.SingleOrDefault(ac => ac.Id == additionalContact.Id);
                if (modifiedContact != null)
                {
                    additionalContact.LastName     = modifiedContact.LastName;
                    additionalContact.FirstName    = modifiedContact.FirstName;
                    additionalContact.EmailAddress = modifiedContact.EmailAddress;
                }
                else
                {
                    existingUser.AdditionalContacts.Remove(additionalContact);
                }
            }

            if (user.AdditionalContacts != null)
            {
                foreach (var potentialNewContact in user.AdditionalContacts)
                {
                    if (!existingUser.AdditionalContacts.Any(ac => ac.Id == potentialNewContact.Id))
                    {
                        existingUser.AdditionalContacts.Add(potentialNewContact);
                    }
                }
            }

            await _dbContext.SaveChangesAsync();

            if (updatedEmail)
            {
                await SendUserInvitationMailAsync(existingUser, confirmationUrlBuilder);
            }

            return(true);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Validates that an assignment is correctly configured.
        /// </summary>
        public async Task <bool> ValidateAssignmentAsync(
            Assignment assignment,
            IModelErrorCollection modelErrors)
        {
            if (assignment.DueDates != null)
            {
                var sections = assignment.DueDates.Select(d => d.SectionId).ToList();
                if (sections.Distinct().Count() != sections.Count)
                {
                    modelErrors.AddError("DueDates", "You may only have one due date per section.");

                    return(false);
                }
            }

            var existingAssignmentQuestions = await _dbContext.AssignmentQuestions
                                              .Where(aq => aq.AssignmentId == assignment.Id)
                                              .ToListAsync();

            foreach (var oldQuestion in assignment.Questions)
            {
                var conflicts = existingAssignmentQuestions
                                .Any
                                (
                    newQuestion => newQuestion.Id == oldQuestion.Id &&
                    newQuestion.QuestionId != oldQuestion.QuestionId
                                );

                if (conflicts)
                {
                    modelErrors.AddError("Questions", "You may not modify an existing question.");
                    return(false);
                }

                _dbContext.Entry(oldQuestion).State = EntityState.Detached;
            }

            var questionNames = assignment.Questions
                                .Select(aq => aq.Name)
                                .ToList();

            if (questionNames.Distinct().Count() != questionNames.Count)
            {
                modelErrors.AddError("Questions", "No two questions may have the same name.");
                return(false);
            }

            if (assignment.CombinedSubmissions)
            {
                var newQuestionIds = new HashSet <int>
                                     (
                    assignment.Questions
                    .Select(aq => aq.QuestionId)
                                     );

                bool anyUnsupportedQuestions = await _dbContext.Questions
                                               .Where(q => newQuestionIds.Contains(q.Id))
                                               .AnyAsync(q => q.UnsupportedSolver(QuestionSolverType.NonInteractive));

                if (anyUnsupportedQuestions)
                {
                    modelErrors.AddError
                    (
                        "CombinedSubmissions",
                        "Submissions may not be combined if the assignment contains any questions "
                        + "that do not support non-interactive submissions (such as code questions)."
                    );

                    return(false);
                }
            }

            if (assignment.CombinedSubmissions && assignment.AnswerInOrder)
            {
                modelErrors.AddError
                (
                    "AnswerInOrder",
                    "The 'Answer In Order' option may not be selected when submissions are combined."
                );

                return(false);
            }

            if (!assignment.CombinedSubmissions && assignment.OnlyShowCombinedScore)
            {
                modelErrors.AddError
                (
                    "OnlyShowCombinedScore",
                    "The 'Only Show Combined Score' option may only be selected when submissions are combined."
                );

                return(false);
            }

            return(true);
        }
Ejemplo n.º 15
0
		/// <summary>
		/// Updates a checkpoint.
		/// </summary>
		private bool UpdateCheckpoint(Checkpoint checkpoint, IModelErrorCollection modelErrors)
		{
			if (checkpoint.SectionDates != null)
			{
				var sections = checkpoint.SectionDates.Select(d => d.SectionId).ToList();
				if (sections.Distinct().Count() != sections.Count)
				{
					modelErrors.AddError("SectionDates", "You may only have one due date per section.");
				}
			}

			if (checkpoint.TestClasses != null)
			{
				var testClasses = checkpoint.TestClasses.Select(tc => tc.TestClassId).ToList();
				if (testClasses.Distinct().Count() != testClasses.Count)
				{
					modelErrors.AddError("TestClasses", "You may only have one entry per test class.");
				}
			}

			if (modelErrors.HasErrors)
			{
				return false;
			}

			_dbContext.RemoveUnwantedObjects
			(
				_dbContext.CheckpointDates,
				checkpointDates => checkpointDates.Id,
				checkpointDates => checkpointDates.CheckpointId == checkpoint.Id,
				checkpoint.SectionDates
			);

			_dbContext.RemoveUnwantedObjects
			(
				_dbContext.CheckpointTestClasses,
				testClass => testClass.Id,
				testClass => testClass.CheckpointId == checkpoint.Id,
				checkpoint.TestClasses
			);

			return true;
		}
Ejemplo n.º 16
0
		/// <summary>
		/// Updates a question.
		/// </summary>
		public async Task<bool> UpdateQuestionAsync(
			string classroomName,
			Question question, 
			IModelErrorCollection errors)
		{
			var classroom = await LoadClassroomAsync(classroomName);

			var questionCategory = await _dbContext.QuestionCategories
				.SingleOrDefaultAsync
				(
					category => category.Id == question.QuestionCategoryId
				);

			if (questionCategory.ClassroomId != classroom.Id)
			{
				throw new InvalidOperationException(
					"Category of question is not in the given classroom.");
			}

			if (await _dbContext.Questions.AnyAsync(
				   q => q.Id != question.Id 
				&& q.Name == question.Name
				&& q.QuestionCategoryId == question.QuestionCategoryId))
			{
				errors.AddError("Name", "Another question with that name already exists.");
				return false;
			}

			await _questionUpdaterFactory.CreateQuestionUpdater(question, errors)
				.UpdateQuestionAsync();

			if (errors.HasErrors)
			{
				return false;
			}

			_dbContext.Update(question);
			await _dbContext.SaveChangesAsync();

			return true;
		}
Ejemplo n.º 17
0
		/// <summary>
		/// Updates a assignment.
		/// </summary>
		private bool UpdateAssignment(Assignment assignment, IModelErrorCollection modelErrors)
		{
			if (assignment.DueDates != null)
			{
				var sections = assignment.DueDates.Select(d => d.SectionId).ToList();
				if (sections.Distinct().Count() != sections.Count)
				{
					modelErrors.AddError("DueDates", "You may only have one due date per section.");

					return false;
				}
			}

			UpdateQuestionOrder(assignment.Questions);

			_dbContext.RemoveUnwantedObjects
			(
				_dbContext.AssignmentQuestions,
				question => question.Id,
				question => question.AssignmentId == assignment.Id,
				assignment.Questions
			);

			_dbContext.RemoveUnwantedObjects
			(
				_dbContext.AssignmentDueDates,
				dueDate => dueDate.Id,
				dueDate => dueDate.AssignmentId == assignment.Id,
				assignment.DueDates
			);

			return true;
		}