Мэтт Гамильтон был прав (см. Выше). Проблема была:
- Свойство Detail должно не создаваться внутри конструктора , а также через методы получения / установки через вспомогательный элемент. Если вам удобно создавать экземпляр Entity, содержащий Properties, в вашем новом экземпляре, то может быть полезно иметь отдельный метод инициализации, который не будет автоматически выполняться Entity Framework, так как он восстанавливает объекты из базы данных.
- Для правильной работы свойства Detail необходимо объявить virtual в мастер-классе.
Следующая БУДЕТ ПРОЙДЕТ (Как и ожидалось / надежда)
public class Master
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid MasterId { get; set; }
//Key new step: Detail MUST be declared VIRTUAL
public virtual Detail Detail { get; set; }
}
public class Detail
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid DetailId { get; set; }
//Set this to be VIRTUAL as well
public virtual Master MyMaster { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Master> Masters { get; set; }
public DbSet<Detail> Details { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//This sets up a BI-DIRECTIONAL relationship
modelBuilder.Entity<Master>()
.HasOptional(x => x.Detail)
.WithOptionalPrincipal(x => x.MyMaster)
.WillCascadeOnDelete(true);
}
}
[TestMethod]
public void UnitTestMethod()
{
var context = new MyDbContext();
context.Database.Delete();
context.Database.CreateIfNotExists();
//Create and save entities
var master = context.Masters.Create();
//Key new step: Detail must be instantiated and set OUTSIDE of the constructor
master.Detail = new Detail();
context.Masters.Add(master);
context.SaveChanges();
//Reload entity
var contextReloaded = new MyDbContext();
var masterReloaded = contextReloaded.Masters.First();
//This NOW Passes, as it should
Assert.AreEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId);
//This line is NO LONGER necessary
contextReloaded.Details.First();
//This shows that there is no change from accessing the Detail from the context
Assert.AreEqual(master.Detail.DetailId, masterReloaded.Detail.DetailId);
}
Наконец, также нет необходимости иметь двунаправленные отношения. Ссылку на «MyMaster» можно безопасно удалить из класса Detail, а вместо этого можно использовать следующее сопоставление:
modelBuilder.Entity<Master>()
.HasOptional(x => x.Detail)
.WithMany()
.IsIndependent();
С учетом вышеизложенного, выполнение context.Details.Remove (master.Detail) привело к тому, что master.Detail == null был истинным (как и следовало ожидать / надеюсь).
Я думаю, что некоторая путаница возникла из сопоставления X-to-many, где вы можете инициализировать виртуальный список сущностей в конструкторе (например, вызывая myDetails = new List ();), потому что вы не создаете экземпляр сами сущности.
Кстати, в случае, если у кого-то возникли сложности с однонаправленной картой «один ко многим» от Мастера до СПИСОК деталей, мне помогло следующее:
modelBuilder.Entity<Master>()
.HasMany(x => x.Details)
.WithMany()
.Map((x) =>
{
x.MapLeftKey(m => m.MasterId, "MasterId");
x.MapRightKey(d => d.DetailId, "DetailId");
});
Ура, Роб