EF Code First:一对多关系更新导航对象

时间:2018-02-11 14:29:24

标签: entity-framework ef-code-first dbcontext

让我通过一个例子解释我的问题:

我有以下实体:

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);
    }
}

从不同的上下文添加/更新/删除分支和项目实体。项目在没有分支的情况下出现,但在一些工作流程之后,项目需要从分支列表中附加到分支。

如果我试试这个,它可以正常工作:

    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);
        }
    }

现在,我理解这是一种预期的行为,因为Project的Branch属性未初始化。 但我的问题是,如何在保存项目之前获取BranchId后强制更新分支(此时一些处理仍然保留在项目中,因此无法保存只是为了到达分支)。希望我能清楚地解释自己。

1 个答案:

答案 0 :(得分:2)

至少有两种方法可以实现目标。

首先使用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)。