我首先使用代码使用WebAPI 2.0和EF6.0编写API。 所以它必然是一个断开连接的dbcontext场景。
我的域名如下
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; }
}
用户界面将允许患者选择不同的营养师,如果她对当前的营养师不满意。所以我的API需要支持这一点。我推测这将是对患者的简单更新。但是单元测试失败了。
[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);
}
}
我希望SaveChanges()
可以保存患者p.Email
和p.Nutritionist
两个属性的更改。但是在数据库中,只有电子邮件被更改,Nutritionist_Id
字段继续显示旧值。因此,由于某种原因,dbContext忽略了对导航属性p.Nutritionist
SQL分析器显示由EF
触发的以下UPDATE查询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
不知道我可能错过了什么。
答案 0 :(得分:2)
是的我可以使用显式属性p.NutritionistId
,但我只是想成为纯粹主义者:)
我发现以下代码有效......
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();
在这种情况下,EF似乎不会自动附加相关条目... p.Nutritionist
。
同样如上面评论中提到的Gert Arnold,也没有加载它。
因此明确加载p.Nutritionist
然后使用附加实体更改其值会使EF感到高兴。
我仍然想知道为什么EF不会自行完成所有这些,因为它似乎是一个逻辑编程意图,当我附加一个新实体并将其标记为EntityState.Modified
时,我想要保存整个它而不仅仅是标量属性。