Sunday, 11 June 2017

asp.net - DbContext.SaveChanges



I have an issue where inserting one record to the entity ProfessionalEmploymentRecord EF ends up duplicating existing Professional, Program, Role records in each entitiy, respectively. I have tried to explicitely initilize Professional, Program, Role with existing key/value pairs in my database, but EF still creates duplicate values with new keys.



public class ProfessionalEmploymentRecord
{

public int ProfessionalEmploymentRecordId {get; set; }

public virtual Professional Professional { get; set; }
public virtual Program Program { get; set; }
public virtual Role Role { get; set; }
}
public class Program
{
public int ProgramId { get; set; }
[MaxLength(20)]

public string Name { get; set; }
}
public class Role
{
public int RoleId { get; set; }
[MaxLength(50)]
public string Name { get; set; }
}
public class Professional
{

public int ProfessionalId { get; set; }
[MaxLength(20)]
public string Name { get; set; }
}


When using Create below I end up with new records in the three related entities although they exist and have a key set. In other words I end up with copies of values with new primary keys (incremented by one) in the three related entities.



    public ActionResult Create(ProfessionalEmploymentRecord professionalemploymentrecord)
{

if (ModelState.IsValid)
{
db.ProfessionalEmploymentRecords.Add(professionalemploymentrecord);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(professionalemploymentrecord);
}



As requested



@model My.Models.ProfessionalEmploymentRecord
@{
ViewBag.Title = "Create";
}

Create


@using (Html.BeginForm()) {
@Html.ValidationSummary(true)


Professional Employment Record


Professional


@Html.EditorFor(model => model.Professional.Name)
@Html.ValidationMessageFor(model => model.Professional.Name)



Program


@Html.EditorFor(model => model.Program.Name)
@Html.ValidationMessageFor(model => model.Program.Name)


Role



@Html.EditorFor(model => model.Role.Name)
@Html.ValidationMessageFor(model => model.Role.Name)





}



@Html.ActionLink("Back to List", "Index")



I have used code below to test. (I see that executing SaveChanges sets *Id++ in reco).



    public void TestDummyAdd()
{
Professional prof = new Professional { ProfessionalId = 1, Name = "Chris" };
Program prog = new Program { ProgramId = 1, Name = "A" };

Role role = new Role { RoleId = 1, Name = "B" };
ProfessionalEmploymentRecord reco = new ProfessionalEmploymentRecord { Professional = prof, Role = role, Program = prog, ProfessionalEmploymentRecordId = 0 };

if (ModelState.IsValid)
{
db.ProfessionalEmploymentRecords.Add(reco);
db.SaveChanges();
}
}


Answer



Add is operation executed on whole object graph. If you add your record you are also adding all its relations, their relations, etc. There are two options how to solve this issue and both require you to manually set state of some entities (and sometimes also relations) in your persisted object grap:




  • Call Add as you did but after that you must set state for all existing entities in the graph to either Unchanged or Modified (entity will be udpated in database)

  • Call Attach instead of Add and after that set state for all new entities to Added



Settings state can be done by:




db.Entry(entity).State = EntityState.Added;

No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...