dbcontext déconnecté ne met pas à jour la propriété de navigation
Question
J'écris une API en utilisant WebAPI 2.0 et EF6.0 en utilisant d'abord le code.C'est donc nécessairement un scénario dbcontext déconnecté.
Mon domaine est comme ci-dessous
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; }
}
L'interface utilisateur permettra au patient de sélectionner un autre nutritionniste s'il n'est pas satisfait du nutritionniste actuel.Mon API doit donc prendre en charge cela.J'ai supposé qu'il s'agirait d'une simple mise à jour sur le patient.Mais le test unitaire suivant échoue.
[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);
}
}
Je m'attendais à ça SaveChanges()
enregistrerait les modifications apportées aux deux propriétés du patient p.Email
et p.Nutritionist
.Mais dans la base de données, seul l'email est modifié et Nutritionist_Id
le champ continue d’afficher l’ancienne valeur.Donc, pour une raison quelconque, dbContext ignore les modifications apportées à la propriété de navigation p.Nutritionist
Le profileur SQL affiche la requête UPDATE suivante lancée par 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
Je me demande ce que je pourrais rater.
La solution
Oui, je pourrais utiliser une propriété explicite p.NutritionistId
mais j'essayais juste d'être un puriste :)
J'ai découvert que le code suivant fonctionne...
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();
Il semble que EF ne fonctionne pas automatiquement attacher les entrées associées... p.Nutritionist
dans ce cas.
Aussi comme Gert Arnold mentionné dans le commentaire ci-dessus, et il ne le charge pas non plus.
Donc charger explicitement le p.Nutritionist
puis changer sa valeur avec l'entité attachée rend EF heureux.
Je me demande toujours pourquoi EF ne ferait pas tout cela tout seul, car il semble logique que lorsque j'attache une nouvelle entité et la marque comme EntityState.Modified
, je veux sauvegarder l'intégralité plutôt que seulement les propriétés scalaires.