在数据库中,表与表之间可能存在多种联系,比如,一对多,多对多的关系。当我们使用逻辑外键在数据库建立两张表之间的关系的时候,我们使用EF实体框架
必然也会将这种关系映射到我们的实体关系中来。所以,在我们做项目的时候,很多情况下我们都使用逻辑外键建立两张表之间的关系,从而避免删除等操作带来的种
种问题。
那么,我们的EF实体框架是怎么解决这种关联关系呢?假如给我们两张表,一张是用户表,另外一张是订单表。用户表与订单表是一对多的关系,我们来仿照EF
代码简单的写一个自己的框架(CodeOnly)
第一步:建两个类UserInfo和Order类
下面这时UserInfo类:
1 namespace codeOnly 2 { 3 public class UserInfo 4 { 5 [Key] 6 public int Id { get; set; } 7 public string UName { get; set; } 8 //这里显示了一对多的关系,一个用户可以有多个Order表 9 public ICollection<Order> Order { get; set; } 10 } 11 }
下面这是Order类
namespace codeOnly { public class Order { [Key] public int Id { get; set; } public string Content { get; set; } //一个Order对应一个User public UserInfo User { get; set; } } }
这样便解决了一对多的关系,多对多的实现也是如此,那么我们再来看看它的上下文的实现过程吧
1 namespace codeOnly 2 { 3 public class DbContentDemo:DbContext 4 { 5 //实现了.Net与数据库的连接 6 public DbContentDemo() 7 : base("name=Demo") 8 { 9 10 } 11 public DbSet<UserInfo> UserInfo { get; set; } 12 public DbSet<Order> Order { get; set; } 13 } 14 }
看到这里,我们是不是就想起了HttpContext这个类呢,简直是具有异曲同工之处呢。前边也已经介绍了上下文的作用,这里就不再多说了,主要介绍一下其
中“name=Demo”的作用。在App.config下:Demo就是与数据库进行连接的字符串。所以它在内部实现了与数据库进行连接的操作。下面这段代码就可以实现在数
据库中创建与我们的实体一一对应的表格了。
1 namespace codeOnly 2 { 3 class Program 4 { 5 public static void Main(string[] args) 6 { 7 DbContentDemo dbcontext = new DbContentDemo(); 8 //初始化数据库 9 dbcontext.Database.CreateIfNotExists();//如果数据库不存在则自动创建 10 UserInfo user = new UserInfo(); 11 user.Id = 5; 12 user.UName = "大家好"; 13 dbcontext.SaveChanges(); 14 Console.ReadKey(); 15 } 16 }
上面这些代码颇为简单,相信大家都能看明白EF实体框架内部的处理过程是怎样的了(这是ModelFirst的情况),那么在DbFirst的情况下处理一对多多对多的关系
我们也简单的用一段小代码来诠释吧。
1 namespace ModelFirstaaaa 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //一对多的关系 8 Model1Container dbContext=new Model1Container(); 9 UserInfo user=new UserInfo(); 10 user.ID = 5; 11 user.IsDel = "false"; 12 user.Phone = "13363605745"; 13 user.SubTime=DateTime.Now; 14 user.UName = "大家好"; 15 dbContext.UserInfo集.AddObject(user); 16 17 Order order=new Order(); 18 order.ID = 5; 19 order.Content = "你哈"; 20 order.UserInfo = user; 21 22 dbContext.SaveChanges(); 23 Console.WriteLine("执行成功"); 24 Console.ReadKey(); 25 26 //多对多的关系 27 28 #region 多对多的关系 29 Model1Container dbContext = new Model1Container(); 30 UserInfo user = new UserInfo(); 31 user.ID = 6; 32 user.IsDel = "false"; 33 user.Phone = "13363605745"; 34 user.SubTime = DateTime.Now; 35 user.UName = "me"; 36 dbContext.UserInfo集.AddObject(user); 37 38 Order order = new Order(); 39 order.Content = "你好"; 40 order.ID = 7; 41 dbContext.Order.AddObject(order); 42 43 order.UserInfo.Add(user); 44 dbContext.SaveChanges(); 45 Console.WriteLine("ok"); 46 #endregion 47 } 48 } 49 }
另外,EF还有一个很优美的特定,那就是延迟加载的特性。EF 中第一个延迟加载机制:当我们用到的时候才会去加载数据。每次使用的时候都会全新为我们加
载数据。使用多少次便查询多少次 。当我们使用Linq表达式写一个查询语句的时候,返回的结果基本上都是一个 IQuerable 类型集合,数据库的sql执行的顺序一般为from,
where,select......而我们EF实体框架将程序编译成sql脚本的时候 先把执行的顺序根据算法生成树的形式(编译原理),根节点是 from, 先中间,后左边,然后最右边。
有可能继续 而我们 C# 提供的 Expression 类型就为我们提供了树的结构。我们写的 Linq 表达式最终只不过是将 Linq 生成了表达式放到了 Expression 表达式类型中去了。放到了
IQueryable 内部中去了。我们用到的 IQueryable 接口的集合的时候, IQuerable 集合内部的 Provider 属性会解析 Expression, 然后生成相应的sql语句去做相应查询并加载数据。
1 var data = dbContext.ClassInfo.Where(c => c.ClassName.Contains("计算机")); 2 foreach (var classinfor in data) 3 { 4 Console.WriteLine(classinfor.ClassId+classinfor.ClassName); 5 }
2.第二种延迟加载机制:
导航属性的延迟加载机制:如果实体是查询出来的,那么通过导航属性去访问其它有关联的实体的时候 EF 会自动帮我们去查询关联表的数据。不管有没有 ToList,
导航属性 有个需求:缓存。 IQueryable 是一个非常特殊的接口,数据并不存放在本地,只有用到的时候通过 Provinder 去解析加载数据 ( 它本身是不存储数据的 ) 。而我们
List , Array(实现了IEnumrable接口,可以对数组进行遍历) 都是一个本地集合 。Var users=(from u in dbHmEntities select u).ToList();当查询成功的 时候就相
当于将数据库中的内容立刻加载到内存中,这时候使用 foreach 遍历集合的时候就不会去执行 sql 脚本了。 FirstOrDefault 也是立即去查询。
1 //IQueryable:是非常特殊的接口,数据并不存放在本地,只有用到的时候去解析Exp加载数据。 2 var data2 = dbContext.ClassInfo.Where(c => c.ClassName.Contains("计算机")).ToList(); 3 foreach (var classinfo in data2) 4 { 5 Console.WriteLine(classinfo.ClassName+classinfo.ClassId); 6 }