Question

I am writing an API using WebAPI 2.0 and EF6.0 using code first. So its necessarily a disconnected dbcontext scenario.

My domain is as as below

public class Patient
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email ( get; set; }
    public virtual Nutritionist Nutritionist { get; set; }
}

public class Nutritionist
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual List<Patient> Patients { get; set; }
}

The UI will allow Patient to select a different Nutritionist if she is not happy with current one. So my API needs to support this. I presumed this would be a simple update on the Patient. But following unit test fails.

    [TestMethod]
    public void Changing_Nutritionist_In_Disconnected_Context_Works()
    {
        Patient p;
        Nutritionist n;
        using (var c = new Context())
        {
            // Get a patient where I know p.Nutritionist.Id == 1 in database.
            p = c.Patients.Find(1);

            // Get some other nutritionist
            n = c.Nutritionists.Find(3);
        }
        using (var c = new Context())
        {
            //change patient's email and nutritionist 
            p.Email = "patient@domain.com";
            p.Nutritionist = n;

            c.Patients.Attach(p);
            c.Entry(p).State = EntityState.Modified;
            c.SaveChanges();
        }
        using (var c = new Context())
        {
            Assert.AreEqual(3,c.Patients.Find(1).Nutritionist.Id);
        }
    }

I expected that SaveChanges() would save changes to both properties of the patient p.Email and p.Nutritionist. But in database, only the email is changed and Nutritionist_Id field continues to show old value. So for some reason, dbContext is ignoring changes to navigation property p.Nutritionist

The SQL profiler shows following UPDATE query fired by EF

exec sp_executesql N'UPDATE [dbo].[Patients]
SET [Name] = @0, [Email] = @1
WHERE ([Id] = @2)
',N'@0 nvarchar(max) ,@1 nvarchar(max) ,@2 int',@0=N'some name',@1=N'patient@domain.com',@2=1
go

Wonder what i could be missing out on.

Was it helpful?

Solution

Yes I could use an explicit property p.NutritionistId but I was just trying to be a purist :)

I found out that following code works...

cn.Patients.Attach(p);
cn.Nutritionists.Attach(n);
cn.Entry(p).Reference(x => x.Nutritionist).Load();
p.Nutritionist = n;
cn.Entry(p).State = EntityState.Modified;
cn.SaveChanges();

It seems that EF does not automatically attach the related entries... p.Nutritionist in this case.

Also as Gert Arnold mentioned in the comment above, nor does it load it.

So explicitly loading the p.Nutritionist and then changing its value with and attached entity makes EF happy.

I am still wondering why EF would not do all this on its own as it seems a logical programming intent that when I attach a new entity and mark it as EntityState.Modified, I want to save the whole of it rather than just scalar properties.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top