实体框架 - 一对多关系的麻烦

时间:2015-04-30 12:21:38

标签: c# entity-framework lazy-loading virtual one-to-many

我们正在采用代码优先的方法,并且在与一对多关系方面遇到麻烦。

  • Departement(Dep)有一份员工名单。
  • 每个员工都属于一个部门。 (必需)
  • 我想更新Employees名称。 (不接触Dep)

我已将示例缩减为一些简单的类:

public class Employee
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string name { get; set; }

    [Required]
    public virtual Dep dep { get; set; }

}

public class Dep
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string name { get; set; }

    public virtual ICollection<Employee> Employees { get; set; }
}

添加新值的工作原理如下:

Dep dep = new Dep { name = "My Departement" };
Employee employee = new Employee { name = "My name" };
dep.Employees.Add(employee);

context.Deps.Add(dep);
context.SaveChanges();

但是,如果我们想要更新员工,我们就会遇到麻烦。

// Get Employee by Id
Employee employee = Repository.Employees.Single(p => p.Id ==<some id>);
employee.name = "My new name";
context.SaveChanges();

问题1:

如果我们使用

  • public virtual Dep dep {get;组; }

与上面的代码一样,我们注意到在调试时可以从Employee获得Dep对象。当我们浏览对象时,Dep被正确加载。

但是如果我们从Employee-class中删除virtual,就像这样:

  • public Dep dep {get;组; }

,然后Dep始终为null。奇怪!

为什么会这样?是不是虚拟只是为了决定相关数据是否应该延迟加载?

问题2

由于我们仍然想要使用虚拟,所以麻烦从保存更新的Employee开始。它始终失败,并显示需要Dep的消息。员工已经有了Dep,我不想搞砸它。我只想更改员工的姓名。没有别的......

为什么在保存员工时会丢失Dep关系?

我可以在使用虚拟时使其工作,如果我确保Dep也是由

手动加载的
  1. 添加一些代码以读取Dep对象中的某些值
    1. 调试和浏览Dep对象
    2. 但这真的很有必要吗?在这种情况下,我真的不关心Dep关系。我为什么要加载Dep-data?

2 个答案:

答案 0 :(得分:1)

问题2:

这是因为您使用[Required]属性。在这种情况下,您要求框架(不仅是EF)处理所需的约束。

如果您只想在EF级别应用约束,则应使用流畅的API:

public class testEF : DbContext {
    public IDbSet<Employee> Employees { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        modelBuilder.Configurations.Add(new EmployeeEFConfiguration());
    }
}

public class EmployeeEFConfiguration : EntityTypeConfiguration<Employee> {
    public EmployeeEFConfiguration() {
        HasRequired(x => x.Dep).WithMany(y => y.Employees);
    }
}

我刚做了测试。以前的代码运行。如果我使用RequiredAttribute,我会遇到与你相同的异常。

完整样本:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Linq;

namespace testEf6 {
    class Program {
        static void Main(string[] args) {
            using (testEF ctx = new testEF()) {
                C2 c2 = new C2 {
                    Name = "old name",
                    C1 = new C1 { }
                };

                ctx.C2s.Add(c2);
                ctx.SaveChanges();
            }

            using (testEF ctx = new testEF()) {
                C2 c2 = ctx.C2s.First();
                c2.Name = "new name";
                ctx.SaveChanges(); // exception here with the attribute ========
            }
        }
    }

    public class testEF : DbContext {
        public IDbSet<C2> C2s { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            //modelBuilder.Configurations.Add(new C2EFConfiguration());
        }
    }

    public class C2EFConfiguration : EntityTypeConfiguration<C2> {
        public C2EFConfiguration() {
            HasRequired(x => x.C1).WithMany(y => y.C2s);
        }
    }

    public class C1 {
        public int Id { get; set; }
        public virtual ICollection<C2> C2s { get; set; }
    }

    public class C2 {
        public int Id { get; set; }
        public String Name { get; set; }
        [Required] 
        public virtual C1 C1 { get; set; }

    }
}

答案 1 :(得分:0)

问题1:virtual启用延迟加载,这意味着当您从数据库中检索记录时,其相关实体也将被带入。 https://msdn.microsoft.com/en-us/data/jj574232.aspx

问题2:

你可以试试这个:

Employee employee = Repository.Employees.Include("Dep").Single(p => p.Id ==<some id>);
var entry = context.Entry(employee);
entry.Property(e => e.name ).IsModified = true;
// other changed properties
db.SaveChanges();