EF Code First:一对多关系更新导航对象
EF Code First : One to Many relationship update the navigation object
让我通过一个例子来解释我的问题:
我有以下实体:
public class Project
{
public int ProjectId { get; set; }
public string ProjectName { get; set; }
public int? BranchId { get; set; }
public virtual Branch Branch { get; set; }
}
public class Branch
{
public int BranchId { get; set; }
public string BranchName { get; set; }
public virtual ICollection<Project> Projects { get; set; }
public Branch()
{
Projects = new ObservableCollection<Project>();
}
}
我的 DbContext 为:
public class MyContext : DbContext
{
public DbSet<Project> Projects { get; set; }
public DbSet<Branch> Branches { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Branch>().HasMany<Project>(b => b.Projects).WithOptional(p => p.Branch).HasForeignKey(p => p.BranchId);
}
}
从不同的上下文中添加/更新/删除了 Branch 和 Project 实体。项目在没有分支的情况下存在,但经过一些工作流程后,需要将项目附加到给定分支列表中的分支。
如果我尝试这个,它工作正常:
private void ForeignKeyTest()
{
using (var db = new MyContext())
{
var project = db.Projects.Find(1);
project.BranchId = 8;
db.SaveChanges();
var msg = project.Branch.BranchName; //call to the Branch after save
MessageBox.Show(msg);
}
}
但是,如果我尝试这样做,我会得到 NullReference 异常:
private void ForeignKeyTest()
{
using (var db = new MyContext())
{
var project = db.Projects.Find(1);
project.BranchId = 8;
var msg = project.Branch.BranchName; //call to the Branch before save
db.SaveChanges();
MessageBox.Show(msg);
}
}
现在,我知道这是一种预期行为,因为项目的分支 属性 未初始化。 但我的问题是,如何在保存项目之前获取BranchId后强制更新分支(此时对项目的一些处理仍然存在,因此无法保存只是为了到达分支)。希望我清楚地解释了自己。
至少有两种方法可以实现目标。
首先是使用 DbReferenceEntry<TEntity, TProperty>.Load
方法明确强制(重新)加载有问题的导航 属性:
var project = db.Projects.Find(1);
project.BranchId = 8;
db.Entry(project).Reference(e => e.Branch).Load(); // <--
var branchName = project.Branch.Name;
其次是使用 DbChangeTracker.DetectChanges
方法强制更新所有已修改的导航属性:
var project = db.Projects.Find(1);
project.BranchId = 8;
db.ChangeTracker.DetectChanges(); // <--
var branchName = project.Branch.Name;
这两种方法都适用于您的方案,但第一种方法更可靠 - 使用或不使用代理都适用,也适用于新实体(例如 db.Projects.Add
)。
让我通过一个例子来解释我的问题:
我有以下实体:
public class Project
{
public int ProjectId { get; set; }
public string ProjectName { get; set; }
public int? BranchId { get; set; }
public virtual Branch Branch { get; set; }
}
public class Branch
{
public int BranchId { get; set; }
public string BranchName { get; set; }
public virtual ICollection<Project> Projects { get; set; }
public Branch()
{
Projects = new ObservableCollection<Project>();
}
}
我的 DbContext 为:
public class MyContext : DbContext
{
public DbSet<Project> Projects { get; set; }
public DbSet<Branch> Branches { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Branch>().HasMany<Project>(b => b.Projects).WithOptional(p => p.Branch).HasForeignKey(p => p.BranchId);
}
}
从不同的上下文中添加/更新/删除了 Branch 和 Project 实体。项目在没有分支的情况下存在,但经过一些工作流程后,需要将项目附加到给定分支列表中的分支。
如果我尝试这个,它工作正常:
private void ForeignKeyTest()
{
using (var db = new MyContext())
{
var project = db.Projects.Find(1);
project.BranchId = 8;
db.SaveChanges();
var msg = project.Branch.BranchName; //call to the Branch after save
MessageBox.Show(msg);
}
}
但是,如果我尝试这样做,我会得到 NullReference 异常:
private void ForeignKeyTest()
{
using (var db = new MyContext())
{
var project = db.Projects.Find(1);
project.BranchId = 8;
var msg = project.Branch.BranchName; //call to the Branch before save
db.SaveChanges();
MessageBox.Show(msg);
}
}
现在,我知道这是一种预期行为,因为项目的分支 属性 未初始化。 但我的问题是,如何在保存项目之前获取BranchId后强制更新分支(此时对项目的一些处理仍然存在,因此无法保存只是为了到达分支)。希望我清楚地解释了自己。
至少有两种方法可以实现目标。
首先是使用 DbReferenceEntry<TEntity, TProperty>.Load
方法明确强制(重新)加载有问题的导航 属性:
var project = db.Projects.Find(1);
project.BranchId = 8;
db.Entry(project).Reference(e => e.Branch).Load(); // <--
var branchName = project.Branch.Name;
其次是使用 DbChangeTracker.DetectChanges
方法强制更新所有已修改的导航属性:
var project = db.Projects.Find(1);
project.BranchId = 8;
db.ChangeTracker.DetectChanges(); // <--
var branchName = project.Branch.Name;
这两种方法都适用于您的方案,但第一种方法更可靠 - 使用或不使用代理都适用,也适用于新实体(例如 db.Projects.Add
)。