I have following two classes: There are 1 to many relationship between project and schedulePhases. I am trying to remove phases from SchedulePhases collection. But it throws an exception on SaveChanges().
Exception:
An error has occurred. The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted. System.InvalidOperationException at System.Data.Entity.Core.Objects.ObjectContext.PrepareToSaveChanges(SaveOptions options) at System.Data.Entity.Core.Objects.ObjectContext.d__31.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at AddProjectODataService.Controllers.ProjectsController.<Patch>d__a.MoveNext() in c:\Workspace\VS2013\POC\AddProjectODataService\AddProjectODataService\Controllers\ProjectsController.cs:line 142 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter
1.GetResult() at System.Threading.Tasks.TaskHelpersExtensions.d__31.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter
1.GetResult() at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter
1.GetResult() at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()
Project.cs:
public class Project
{
public Project()
{
SchedulePhases = new HashSet<SchedulePhase>();
}
public void Initialize()
{
CalculateSchedule();
}
[Key]
public decimal ProjectId { get; set; }
public decimal AssetId { get; set; }
public decimal CapitalCategoryId { get; set; }
public decimal ProjectTypeId { get; set; }
public virtual Asset Asset { get; set; }
public virtual ICollection<SchedulePhase> SchedulePhases { get; set; }
public virtual CapitalCategory CapitalCategory { get; set; }
public virtual ProjectType ProjectType { get; set; }
public void CalculateSchedule()
{
List<SchedulePhase> SchedulePhaseList = new List<SchedulePhase>();
SchedulePhase sp = new SchedulePhase();
if (this.ProjectTypeId == 15)
{
if (this.SchedulePhases.Count > 0)
{
foreach (SchedulePhase phase in SchedulePhases.ToList())
{
SchedulePhases.Remove(phase);
}
}
}
}
SchedulePhase.cs:
public class SchedulePhase
{
public SchedulePhase()
{
}
[Key]
public decimal SchedulePhaseId { get; set; }
public decimal ProjectId { get; set; } //FK
public decimal PhaseTypeId { get; set; }
public DateTime StartDate { get; set; }
[StringLength(1)]
public string ActualEstimateCalcStart { get; set; }
public DateTime EndDate { get; set; }
[StringLength(1)]
public string ActualEstimateCalcEnd { get; set; }
public decimal Duration { get; set; }
public decimal? CostingYear { get; set; }
public decimal? Cost { get; set; }
public decimal OffSet { get; set; }
public virtual Project Project { get; set; }
public virtual PhaseType PhaseType { get; set; }
}
Here is my patch operation in ProjectController.cs (I am using Odata V3 with web api) :
// PATCH: odata/Projects(5)
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] decimal key, Delta<Project> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Project project = await db.Projects.FindAsync(key);
if (project == null)
{
return NotFound();
}
patch.Patch(project);
project.Initialize();
try
{
await db.SaveChangesAsync(); //Above Exception thrown here.
}
catch (DbUpdateConcurrencyException)
{
if (!ProjectExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(project);
}
I have read few articles but not sure what to do to resolve in my scenario.
I have referred following articles: http://blogs.msdn.com/b/dsimmons/archive/2010/01/31/deleting-foreign-key-relationships-in-ef4.aspx http://msdn.microsoft.com/en-us/data/jj713564.aspx Entity Framework .Remove() vs. .DeleteObject() ( In this, User suggested to explicitly delete the child with DeleteObject. I am using DbContext.)
Any suggestion?
Thanks,