【EF】2.主外键关系导航属性
一.数据准备
公司表Company和员工表Employee两张表创建主外键强关系,员工表Employee的Company_ID是公司表Company的外键,如下图所
二.解读主外键生成的实体特殊性
Company实体-主外键关系的话,EF生成的实体,主表Company实体有个子表Employee的集合,注意这个集合是virtual虚拟的
Employee实体-子表里面还有个主表Company的实例,引用属性,注意主表实体Company也是virtual虚拟的
三.导航属性是延迟查询
我们看到我们只查询公司ID是“0000000001”的公司,并没有主动去查询这个公司下面的员工并没有生成查询员工的sql,但是当我们遍历公司员工company.Employee的时候EF主动帮我们查询了得到这个公司下面的员工,所以证明导航属性是延迟查询,只有在真正使用的时候才去查询“0000000001”这个公司下面的员工,条件是virtaul属性(Employees是虚拟的)+ 默认配置(dbContext.Configuration.LazyLoadingEnabled = true;懒加载配置是true)
public class EFNavTest { public static void Show() { { using (SchoolDBEntities dbContext = new SchoolDBEntities()) { var companyList = dbContext.Set<Company>().Where(c => c.Company_ID == "0000000001"); foreach (var company in companyList)//只查company { Console.WriteLine(company.Company_Name); foreach (var employee in company.Employees) { Console.WriteLine(employee.Employee_Name); } } Console.Read(); } } } }
四.关闭延迟加载,子表的数据就没有了
dbContext.Configuration.LazyLoadingEnabled = false;
{ using (SchoolDBEntities dbContext = new SchoolDBEntities()) { dbContext.Configuration.LazyLoadingEnabled = false;//关闭延迟查询 var companyList = dbContext.Set<Company>().Where(c => c.Company_Id == 02); foreach (var company in companyList)//只查company { Console.WriteLine(company.Company_Name); foreach (var employee in company.Employees) { Console.WriteLine(employee.Employee_Name); } } } }
五.Include预先加载
预先加载,Include查询主表Company时就把子表数据Employee一次性查出来,和上面导航属性的懒加载不同,不同之处在于上面是当使用company.Employee的时候EF主动帮我们查询了得到这个公司下面的员工,而Include查询主表Company时就把子表数据Employee一次性查出来了不管你有没有用,我们从EF生成的sql语句就能看出来是个左外连接,它是把“0000000001”这个公司和它下面的员工一下都查询出来了
{ //3、预先加载,Include查询主表时就把子表数据一次性查出来 using (SchoolDBEntities dbContext = new SchoolDBEntities()) { dbContext.Configuration.LazyLoadingEnabled = false;//是否关闭无所谓 var companyList = dbContext.Set<Company>().Include("Employees").Where(c => c.Company_Id == "0000000001"); foreach (var company in companyList)//只查company { Console.WriteLine(company.Company_Name); foreach (var employee in company.Employees)
{ Console.WriteLine(employee.Employee_Name);
} } } }
六.关闭延迟查询后,不想使用Include一次性查询子表的数据,只是需要子表的数据,才要查询,Collection可以显式加载
当执行了dbContext.Entry<Company>(company).Collection(c => c.Employees).Load();这句代码,就会生成查询该公司下的员工的sql语句,就会查询该公司的员工
{ //4、关闭延迟查询后,如果需要子表数据,可以显示加载 using (SchoolDBEntities dbContext = new SchoolDBEntities()) { dbContext.Configuration.LazyLoadingEnabled = false; var companyList = dbContext.Set<Company>().Where(c => c.Company_Id == "0000000001"); foreach (var company in companyList)//只查company { Console.WriteLine(company.Company_Name); dbContext.Entry<Company>(company).Collection(c => c.Employees).Load(); foreach (var employee in company.Employees) { Console.WriteLine(employee.Employee_Name); } } } }
七.关闭延迟查询后,如果需要主表数据,Reference可以显式加载
当执行了dbContext.Entry(employee).Reference(s => s.Company).Load();这句代码,就会生成查询该员工的公司的sql语句,就会查询该员工的公司
//5、关闭延迟查询后、如果需要主表数据,可以显示加载 using (SchoolDBEntities dbContext = new SchoolDBEntities()) { dbContext.Configuration.LazyLoadingEnabled = false; var employeeList = dbContext.Set<Employee>().Where(s => s.Employee_ID == "0000000001"); foreach (var employee in employeeList) { Console.WriteLine(employee.Employee_Name); dbContext.Entry<Employee>(employee).Reference(s => s.Company).Load(); string companyName = employee.Company.Company_Name; Console.WriteLine(companyName); } }
八.结论
根据上面的阐述,可以表与表之间不必建立强关系,可以表与表非主外键关系建立导航属性,一般来说,主表有个子表的集合导航属性(像上述的public virtual ICollection<Employee> Employees { get; set; },注意必须是virtual虚拟的),子表里面还有个主表的实例(像上述的 public virtual Company Company { get; set; },注意必须是virtual虚拟的),引用属性,就可以了