public ReleaseModels.Release SaveReleaseConfiguration(ReleaseConfigurationInputModel obj) { var conn = new SqlConnection("Data Source=localhost\\SQLENTERPRISE;Initial Catalog=Planner;Integrated Security=SSPI;MultipleActiveResultSets=true"); int newId = 0; try { using (conn) { conn.Open(); var cmd = new SqlCommand("sp_upsert_release", conn); cmd.Parameters.Add("@Id", System.Data.SqlDbType.Int).Value = obj.Id; cmd.Parameters.Add("@Title", System.Data.SqlDbType.VarChar).Value = obj.Title; cmd.Parameters.Add("@Descr", System.Data.SqlDbType.VarChar).Value = "";// obj.Descr; cmd.Parameters.Add("@StartDate", System.Data.SqlDbType.DateTime).Value = obj.StartDate.ToDateTimeFromDutchString(); cmd.Parameters.Add("@EndDate", System.Data.SqlDbType.DateTime).Value = obj.EndDate.ToDateTimeFromDutchString(); cmd.Parameters.Add("@IterationPath", System.Data.SqlDbType.VarChar).Value = obj.TfsIterationPath ?? ""; cmd.Parameters.Add("@ParentId", System.Data.SqlDbType.Int).Value = DBNull.Value; cmd.CommandType = System.Data.CommandType.StoredProcedure; var result = cmd.ExecuteScalar().ToString(); if (result == string.Empty) // it's an update { newId = obj.Id; } else // it's an insert { newId = int.Parse(result); } // clean up Milestones that are in database but not present in the inputmodel // do cleaning before storing the new milestone(s) to db, otherwise this / these will be deleted immediately since the newly generated id probably does not match the id passed in the inputmodel var msrep = new MilestoneRepository(); // get all existing milestoneid's for this release var currentMilestones = msrep.GetConfiguredMilestonesForRelease(newId).Select(m => m.Id).ToList(); // get all milestoneId's contained in the inputmodel var newMilestones = obj.Milestones.Select(m => m.Id).ToList(); // remove all new id's from existing id's, keeping all obsolete id's. 'RemoveAll' returns the amount removed, the collection itself is changed var amountObsolete = currentMilestones.RemoveAll(ms => newMilestones.Contains(ms)); foreach (var obsoleteId in currentMilestones) { this.DeleteMilestone(obsoleteId); } foreach (var phase in obj.Phases) { this.SavePhaseConfiguration(phase, newId); } foreach (var milestone in obj.Milestones) { this.SaveMilestoneConfiguration(milestone, newId); } // Projects are not value objects, they can be tracked and maintained seperately. // They do however belong to the Release aggregate but they need to be assigned / unassigned, not just deleted and inserted var projRep = new ProjectRepository(); // get all existing projectid's for this release var currentProjects = projRep.GetConfiguredProjectsForRelease(newId).Select(m => m.Id).ToList(); // create copy to determine obsolete projects var obsoleteProjects = new List<int>(); obsoleteProjects.AddRange(currentProjects); // remove all configured id's in inputmodel from existing id's, keeping all obsolete id's. 'RemoveAll' returns the amount removed, the collection itself is changed var amountObsoleteProjects = obsoleteProjects.RemoveAll(p => obj.Projects.Contains(p)); foreach (var obsoleteId in obsoleteProjects) { this.UnAssignProject(newId, obsoleteId); } // remove current projects from configured projects in inputmodel, leaving all new projects obj.Projects.RemoveAll(p => currentProjects.Contains(p)); // completely renew the Projects for the Release as set in the client app //var cmdDelCross = new SqlCommand(string.Format("Delete from ReleaseProjects where PhaseId = {0}", newId), conn); //cmdDelCross.ExecuteNonQuery(); if (obj.Projects != null && obj.Projects.Count > 0) { var cmdInsertReleaseProject = new SqlCommand("sp_insert_releaseproject", conn); cmdInsertReleaseProject.Parameters.Add("@ReleaseId", System.Data.SqlDbType.Int).Value = newId; cmdInsertReleaseProject.Parameters.Add("@ProjectId", System.Data.SqlDbType.Int).Value = 0; cmdInsertReleaseProject.CommandType = System.Data.CommandType.StoredProcedure; foreach (var projId in obj.Projects) { cmdInsertReleaseProject.Parameters["@ProjectId"].Value = projId; cmdInsertReleaseProject.ExecuteNonQuery(); } } } var rel = this.GetReleaseSummary(newId); this.GenerateStatusRecords(rel); return rel; } catch (Exception ex) { throw; } }