在昨天我学习了LinQ的一些基础知识和动手写了一些LinQ to Object的例子的基础上,对于LinQ语法和基本的要点有了一定的了解。今天继续自己的学习,对于今天学习的LinQ to DataSet 和LinQ to Entity做自己的一些总结,一方面加深自己的理解,另一方面也能掌握LinQ技术的实现机制,对于也跟我一样对着一方面有兴趣的也可以让大家有个初步的感性认识,也是好的。
今天主要的篇幅会讲解LinQ to Entity的C#实现机制以及解决昨天我看完一小节之后的两点疑惑,后面会花一点篇幅对LinQ to DataSet做一个介绍,那具体的LinQ入门基本上就可以已经介绍完了。
看完了整个章节总结一下LinQ to Entity的实现机制,可以粗略的用下面的图来说明:
EntityFrameWork:是一个对象-关系的映射系统,在关系数据库与c#源代码环境中的对象转换起到了至关重要的作用,没有它的LinQ to Entity估计会找不到对象吧,哈哈哈。它可以将关系数据库中的数据库和数据对象全部映射到LinQ to Entity的上下文环境,分别映射为一个ObjectContext类--数据库对象;和对应的ObjectSet对象---对应的数据库对象(表、视图、存储过程、函数),然后我们通过前面学习的LinQ表达式就可以像操作对象一样访问数据和进行数据操作,其原理就在于关系数据库中的表-行-字段其实都可以映射为对象。这或许就是LinQ to Entity的精髓吧。
结合上面的详解可以更好的理解以下的简略框架分析图:
昨天自己学到这一块存在一个疑问,就是LinQ相对ADO.NET的优势在哪里呢?
在今天的学习中,关于LinQ to Entity的深入学习,了解了它的一些特点,关于其他的LinQ to DataSet/XML/Object来说,都是LinQ内存数据操作的独有的特色,关于LinQ to Entity与ADO.net技术两者差别都都是为了实现数据库访问。以下是我总结的一些区别:
共同点:都是实现数据库访问,LinQ to Entity也没有提供任何不能用ADO.NET实现的特性,就是说两个效果都可以实现,具体用什么技术看使用的场景。
区别:1、更少的代码;LinQ to Entity不涉及数据库底层,不需要编写复杂的SQL语句,通过对象方式进行数据库操作,但最后的数据库更新都是通过SQL语句,也就是Linq 语句其实最终都会解析为SQL语句。
2、LinQ to Entity原理是通过操作缓存数据进行数据操作,最后再统一提交解析sql语句执行更细腻,所以特有的一个特点就是可以批量更新。
3、LinQ to Entity的变更跟踪:提供了系统方法,可以跟踪解析提交的SQL语句。
4、灵活的查询能力;因为不是传统的SQL语句拼接,而是使用LinQ查询模型,可以通过一个模型访问不同的数据库【对于这一个特点,整个章节看完了,也没什么场景例子作证对个数据库通用一个查询模型的例子,所以可以暂时不用深究】
接下来会通过实际的例子说明通过LinQ to Entity 实现数据库访问:增 、删 、改 、查询(单表查询、关联查询)
Entity FrameWork依赖于数据库数据模型来使用LinQ查询,表中的行被转换为行对象的实例,每一个记录的列被转换为行对象的属性。数据库的数据模型可以通过Visual Studio来自动生成,也可以通过新增数据模型文件来手动创建映射,这次的例子采用VS生成,这样简单不太容易出错。
选择【从数据库生成】进入下面:
点击继续:
测试连接通过之后,点击确定,会生成一个连接字符串。以下有个单选项一般默认选择【是】进入下一步:
接下来这个是比较重要的一步:我们可以看到此刻自动添加一个dll引用,同时我们看到它将数据库中的表间关系展现的一览无遗,所以这也是LinQ to Entity的很神奇的地方。
结合上面直观的数据模型图,这里说明一下它的构架:之前系统还原,导致电脑上的Viso没了,所以没能用结构化的图形做下面的说明,大家凑合着理解吧。哈哈
一个数据库的数据模型:(1)派生对象的上下文类:也即是前面说的ObjecgContext,可以理解他就是数据库的一个源代码映射。
(2)实体类:也即是前面我提到的ObjectSet,就是我们刚才所选择映射到程序的表、视图、存储过程的源代码映射。
(2.1)其中实体关系跟SQL中的表关系差不多:有一对一,一对多的关系,其关联通过上面看到的导航属性来关联【表外键】
//前端显示客户表中的以下字段,今天的例子都是采用系统数据库northWind中的数据
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false"> <Columns> <asp:BoundField DataField="id" HeaderText="客户ID"/> <asp:BoundField DataField="name" HeaderText="客户姓名"/> <asp:BoundField DataField="city" HeaderText="城市"/> <asp:BoundField DataField="country" HeaderText="国家"/> </Columns> </asp:GridView>
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Collections; using System.Linq; using System.Data.Objects;//obejctQuery实例的命名空间 namespace LinQ { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { //实例化数据库对象ObjectContext NorthwindEntities myTestDB = new NorthwindEntities(); var retRows = from customer in myTestDB.Customers where customer.Country == "USA" select new { id = customer.CustomerID,name=customer.ContactName, city = customer.City, country = customer.Country }; GridView1.DataSource = retRows; GridView1.DataBind(); //需要将查询的结果转换为ObjectQuery实例,才可以跟踪输出SQL语句 TextBox1.Text = (retRows as ObjectQuery).ToTraceString(); } } }View Code
页面显示效果:
转换的SQL语句:
SELECT 1 AS [C1], [Extent1].[CustomerID] AS [CustomerID], [Extent1].[ContactName] AS [ContactName], [Extent1].[City] AS [City], [Extent1].[Country] AS [Country]FROM [dbo].[Customers] AS [Extent1]WHERE N'USA' = [Extent1].[Country]View Code
2. 关联查询:前端文件也需要修改,这里就不粘贴出来了。直接看一下后端LinQ表达式。
关联查询可以通过两个表达式实现:let 和 selectMany扩展方法实现,本次就let方式做演示。
let方式:
protected void Page_Load(object sender, EventArgs e) { //实例化数据库对象ObjectContext NorthwindEntities myTestDB = new NorthwindEntities(); var retRows = from customer in myTestDB.Customers //let方式实现,不知道是不是为了与SQL中的左外连接一致才采用let作为关键字 //不过用法很相似 let leftTab=from order in customer.Orders //这个像不像SQL中的子查询和组过滤表达式,哈哈……太像了 select order where customer.Country == "USA" select new { id = customer.CustomerID,name=customer.ContactName, city = customer.City, country = customer.Country,orderCnt=leftTab.Count() }; GridView1.DataSource = retRows; GridView1.DataBind(); //需要将查询的结果转换为ObjectQuery实例,才可以跟踪输出SQL语句 TextBox1.Text = (retRows as ObjectQuery).ToTraceString(); }View Code
转换的SQL语句:
SELECT 1 AS [C1], [Project1].[CustomerID] AS [CustomerID], [Project1].[ContactName] AS [ContactName], [Project1].[City] AS [City], [Project1].[Country] AS [Country], [Project1].[C1] AS [C2]FROM ( SELECT [Extent1].[CustomerID] AS [CustomerID], [Extent1].[ContactName] AS [ContactName], [Extent1].[City] AS [City], [Extent1].[Country] AS [Country], (SELECT COUNT(1) AS [A1] FROM [dbo].[Orders] AS [Extent2] WHERE [Extent1].[CustomerID] = [Extent2].[CustomerID]) AS [C1] FROM [dbo].[Customers] AS [Extent1] WHERE N'USA' = [Extent1].[Country]) AS [Project1]View Code
3.通过LinQ to Entity新增插入记录:
单表插入可以存在两个方法去是实现一个是通过new 实例化表对象的属性值,一个同通过调用每个表对象的CreatXXXX(col1,col2)构造函数先创建基本的包涵必填项的行记录对象,再以对象属性赋值的方法去不断填充列 属性值,以下做演示的代码数据,只能执行一次,只需要关注其中的代码实现,具体的插入数据而应该是动态交互插入的。
//单表插入,new实例化方法创建一条记录并插入更新 public void insertSignlData() { //实例化数据库对象ObjectContext NorthwindEntities myTestDB = new NorthwindEntities(); Customer myCust = new Customer() { CustomerID="george_liyk", ContactName="小凯哥", City="武汉", Country="USA" //为了前面的数据过滤通过,可以页面显示 }; //提交对象数据保存执行更新 myTestDB.Customers.AddObject(myCust); } //单表插入:Creat[T]调用每个表对象的构造函数创建一条记录并插入更新 public void insertSignlData() { //实例化数据库对象ObjectContext NorthwindEntities myTestDB = new NorthwindEntities(); Customer mycust = Customer.CreateCustomer("george", "无涯子"); mycust.City = "北京"; mycust.Country = "USA"; //提交对象数据保存执行更新 myTestDB.Customers.AddObject(mycust); }View Code
关联表对象数据插入也有两个方法实现:一个是同事实例化两个表对象的的相关字段值(同上面的单表做多次插入而已),一个是在实例化一个表对象的同时,对它的导航属性页初始化。下面的代码演示的是第二种方式。
//关联表数据同时插入方式- public void insertRelatData() { //实例化数据库对象ObjectContext NorthwindEntities myTestDB = new NorthwindEntities(); Customer myCust = new Customer() { CustomerID = "lio", ContactName = "关联表多表插入", City = "武汉", Country = "USA", //为了前面的数据过滤通过,可以页面显示 Orders={ new Order{ CustomerID="lio" //由于order表中id未必填项,其余的字段这里就不多余例举了 } } }; //提交对象数据保存执行更新 myTestDB.Customers.AddObject(myCust); }View Code
4.表更新:
//将刚才插入的客户名【无涯子】改为【哈哈哈】 public void updateData() { //实例化数据库对象ObjectContext NorthwindEntities myTestDB = new NorthwindEntities(); Customer data = (from customer in myTestDB.Customers where customer.CustomerID == "george" select customer).Single(); data.ContactName="哈哈哈"; //提交数据更新 myTestDB.SaveChanges(); }View Code
5.表删除:
//删除george记录 public void deletData() { //实例化数据库对象ObjectContext NorthwindEntities myTestDB = new NorthwindEntities(); Customer george = (from cust in myTestDB.Customers where cust.CustomerID == "george" select cust).Single(); //内存对象中删除记录 myTestDB.Customers.DeleteObject(george); //将此时的更新数据从内存提交数据库更新 myTestDB.SaveChanges(); }View Code
后续:上面的页面展示由于图片要截图很耽搁时间,所以就没有一一截图了,也可以理解,本次的内容都还没写完。。。。
明天后续会将剩余的内容补上。昨天太晚今天得早睡。。。
【下期内容】:LinQ to Entity的并发管理
LinQ to DataSet实现
相关的EntityDataSourse控件介绍