/// <summary> /// Runs the provided package by a set of rules /// </summary> /// <returns>null on success, or an IHttpActionResult on a failure</returns> private IHttpActionResult ValidatePackage(WorkPackageDTO package) { // get the list of child packages var allPackages = new ListSet<WorkPackageDTO>(package.AllChildren) { package // include the root package to make coding life easier }; // load any existing packages from the context var childPackagesFromDb = this._workPackageRepository.GetAll(allPackages.Select(wp => wp.Id).Where(i => i > 0)).ToDictionary(wp => wp.Id); // first pass - replace non modifyable packages with their current data from the db var newPackageCount = -1; var newTaskCount = -1; for (var i = 0; i < allPackages.Count; i++) { var pkg = allPackages[i]; if (childPackagesFromDb.ContainsKey(pkg.Id)) { // we're dealing with an existing package var fromDb = childPackagesFromDb[pkg.Id]; if (fromDb.IsReadOnlyFor(this._currentUser)) { // ignore any changes to packages the user has no right to change allPackages[i] = Mapper.Map<WorkPackageDTO>(fromDb); continue; } else { // note that we don't update the values here - it gets done by the repository } } else { // we're dealing with a new package pkg.Id = newPackageCount--; pkg.CreatedBy = this._currentUserDto; } // make sure each of the tasks have a unique Id foreach (var task in pkg.Tasks.Where(t => t.Id < 0)) { task.Id = newTaskCount--; } } // second pass - update the packages with correct references // note that whilst we don't update the read only package's content (tasks, name, etc) - we do have to make sure it has the correct package references // so that EF doesn't try and add two of the same package var childDict = allPackages.ToDictionary(wp => wp.Id); var processedPackages = new HashSet<WorkPackageDTO>(); foreach (var pkg in allPackages) { if (!Utilities.IsNullOrEmpty(pkg.ChildPackages) && // save a little time in processing packages with no children !processedPackages.Contains(pkg)) // don't reprocess packages { pkg.ChildPackages = new ListSet<WorkPackageDTO>(pkg.ChildPackages.Select(child => childDict[child.Id])); } processedPackages.Add(pkg); } // third pass - check for cycles // we do this last because we may remove a cycle when we clean up readonly packages var packageWithCycle = package.DetectCycles(); if (packageWithCycle != null) { return this.BadRequest(string.Format(Resources.Error_CreatePackage_Cycles, packageWithCycle.Id)); } return null; }