11.3 LINQ to SQL
LINQ to SQL用于以对象形式管理关系数据,并提供了丰富的查询功能。LINQ to SQL建立在公共语言类型系统中基于SQL的模式定义之上,当保持关系型模型表达能力和对底层存储的直接查询评测的性能时,这个集成在关系型数据之上提供强类型。
11.3.1 对象关系设计器简介
对象关系设计器(Object Relation设计器,简称OR设计器)是VS 2012提供的一个可视化设计界面环境,用于创建基于数据库中对象的LINQ to SQL实体类和关联。OR设计器会将数据库对象与LINQ to SQL类之间的映射保存到名为“.dbml”的文件中。
使用OR设计器可以在应用程序中创建映射到数据库对象的对象模型。同时还会生成一个DataContext类用于在实体类与数据库之间发送和接收数据。此外,OR设计器还提供了将存储过程和函数映射到DataContext类的方法以返回数据并填充实体类的功能。OR设计器还支持对实体类之间的继承关系进行设计。
O/R设计器的设计界面有两个不同的区域:左侧的实体窗格以及右侧的方法窗格。实体窗格是主设计界面,其中显示实体类、关联和继承层次结构;方法窗格是显示映射到存储过程和函数的DataContext方法的设计界面。
【范例16】
使用OR对象设计器创建一个到db_Books数据库中Books数据表的映射关联,并显示该表中的图书信息。主要步骤如下所示。
(1)在VS 2012中打开应用程序的【添加新项】对话框。选择其中的【LINQ to SQL类】项,定义名称为dbBooks.dbml,最后单击【添加】按钮,如图11-6所示。
图11-6 【添加新项】对话框
(2)打开【服务器资源管理器】窗格创建一个到SQL Server服务器db_Books数据库的连接。然后展开【表】节点,将Books表选中拖动到右侧的实体窗格。此时会自动生成名为Books的实体,将Books表中的列生成为实体属性,如图11-7所示。
图11-7 OR设计器效果图
提示
通过将数据表从【服务器资源管理器】或者【数据库资源管理器】窗格拖动到OR设计器上可以创建基于这些表的实体类,并由OR设计器自动生成代码。
(3)在Web窗体中添加一个GridView控件。在后台Load事件中使用LINQ查询数据源(Books数据表)中的信息并绑定到GridView控件显示。代码如下。
protected void Page_Load(object sender, EventArgs e) { dbBooksDataContext db = new dbBooksDataContext(); //初始化dbBooksDataContext类 var books = from b in db.Books select new { 编号 = b.Id, 书号 = b.bkno, 名称 = b.name, 出版社 = b.pub, 价格 = b.price }; //创建一个LINQ查询 GridView1.DataSource = books; //为GridView控件绑定数据 GridView1.DataBind(); }
(4)执行程序,在Web页面上会显示出Books数据表中的图书信息,如图11-8所示。
图11-8 显示图书信息运行效果
11.3.2 DataContext类简介
DataContext类是一个LINQ to SQL类,它在SQL Server数据库与映射的数据库实体类之间建立了管道。DataContext类包含用于连接数据库以及操作数据库数据的连接字符串信息和方法。
DataContext类构造函数有很多,常用的构造函数形式如下。
public DataContext(IDbConnection connection)
这种形式将使用连接对象创建DataContext类的实例。
public DataContext(string fileOrServerOrConnection)
这种形式使用连接字符串、数据库所在的服务器的名称(将使用默认数据库)或数据库所在文件的名称创建DataContext类的实例。
public DataContext(IDbConnection connection, MappingSource mapping),
这种形式将使用连接对象和映射源创建DataContext类的实例。
public DataContext(string fileOrServerOrConnection, MappingSource mapping)
使用连接字符串、数据库所在的服务器的名称(将使用默认数据库)或数据库所在文件的名称和映射源创建DataContext类的实例。
1.DataContext类方法
DataContext类包含多个可以调用的方法,例如用于将已更新数据从LINQ to SQL类提交到数据库的SubmitChanges()方法。如表11-4所示列出了DataContext类的常用方法和说明。
表11-4 DataContext类常用方法
例如,下面的示例调用DatabaseExists()方法判断Northwind数据库是否存在,如果存在则调用DeleteDatabase()方法删除该数据库;否则就调用CreateDatabase()方法创建该数据库。
//检测Northwind数据库是否存在 if (db.DatabaseExists()){ Response.Write("Northwind数据库存在"); db.DeleteDatabase(); //删除数据库 } else { Response.Write("Northwind数据库不存在"); db.CreateDatabase(); //创建数据库 }
2.DataContext类属性
在表11-5中列出了DataContext类的常用属性及其说明。
表11-5 DataContext类常用属性
例如,下面的示例使用Log属性在SQL代码执行前在控制台窗口中显示此代码。可以将此属性与查询、插入、更新和删除命令一起使用。
//关闭日志功能 //db.Log = null; //使用日志功能:日志输出到控制台窗口 db.Log = Console.Out; var q = from c in db.Customers where c.City == "London" select c; //日志输出到文件 StreamWriter sw = new StreamWriter(Server.MapPath("log.txt"), true); db.Log = sw; var q = from c in db.Customers where c.City == "London" select c; sw.Close();
11.3.3 SubmitChanges()方法简介
除了使用LINQ对数据表进行查询外,LINQ to SQL还可以对表中的数据进行添加、更新和删除,这与SQL中的INSERT、UPDATE和DELETE语句实现的功能相同。
在使用本地数据时无论对象做了多少项更改,都只是在更改内存中的副本,并未对数据库中的实际数据做任何更改。直到对DataContext显式调用SubmitChanges()方法,所做的更改才会传输到服务器。DataContext会设法将所做的更改转换为等效的SQL命令。
也可以使用自定义逻辑来重写这些操作,但提交顺序是由DataContext的一项称作“更改处理器”的服务来协调的。事件的顺序如下。
(1)当调用SubmitChanges()方法时,LINQ to SQL会检查已知对象的集合以确定新实例是否已附加到它们。如果已附加,这些新实例将添加到被跟踪对象的集合。
(2)所有具有挂起更改的对象将按照它们之间的依赖关系排序成一个对象序列。如果一个对象的更改依赖于其他对象,则这个对象将排在其依赖项之后。
(3)在即将传输任何实际更改时,LINQ to SQL会启动一个事务来封装由各条命令组成的系列。
(4)对对象的更改会逐个转换为SQL命令,然后发送到服务器。
注意
如果数据库检测到任何错误,都会造成提交进程停止并引发异常。将回滚对数据库的所有更改,就像未进行过提交一样。
11.3.4 插入数据
使用LINQ to SQL向数据库中插入数据时必须完成三个步骤:首先要创建一个包含要提交的列数据的新对象,其次将这个新对象添加到与数据库中的目标表关联的LINQ to SQL Table集合,最后将更改提交到数据库。此时,LINQ to SQL会将在应用程序中所做的更改转换成相应的INSERT语句。
【范例17】
在11.3.1节的基础上创建一个新的图书信息对象,并将该对象添加到集合中,最后将所做的更改提交到数据库中,使之成为Books表中的一个新行。
实现代码如下。
private void Page_Load(object sender, EventArgs e) { dbBooksDataContext db = new dbBooksDataContext(); //初始化dbBooksDataContext类 var newBook = new Books { //设置新行中各列的值 Id = 8, bkno = "1008", name = "Java测试", pub = "人民邮电", price = 39 }; db.Books.InsertOnSubmit(newBook); try { db.SubmitChanges(); } catch (Exception ex) { Response.Write("出错了。信息:" + ex.ToString()); } }
上述代码首先创建了一个表示Books表中所有数据的DataContext类db,然后创建了一个Books类对象newBook表示要新增的图书信息。然后调用InsertOnSubmit()方法将newBook插入到db中,最后调用SubmitChanges()方法将修改后的集合提交到Books表。如图11-9所示为添加后的Books表内容。
图11-9 添加后的Books表内容
11.3.5 更新数据
使用LINQ to SQL更新数据库中的数据需要三个步骤:首先要创建一个包含所有数据的集合,然后在集合中对数据进行更改,最后将更改提交到数据库。此时,LINQ to SQL会将所有更改转换成相应的UPDATE语句。
【范例18】
在11.3.4节的基础上对图书进行如下更改.
(1)将编号为5的图书名称修改为“三维动画设计”,价格修改为58。
(2)将所有出版社为“清华大学出版社”的图书价格修改为100。
实现代码如下。
private void btnUpdate_Click(object sender, EventArgs e) { dbBooksDataContext db = new dbBooksDataContext(); //初始化dbBooksDataContext类 Books aBook = (from b in db.Books where b.Id == 5 select b).First(); //查询编号为5的图书 aBook.name = "三维动画设计"; //修改图书名称 aBook.price = 58; //修改图书价格 var upBooks = from b in db.Books where b.pub == "清华大学出版社" select b; foreach (var b in upBooks) { b.price = 100; //批量修改图书价格 } try { db.SubmitChanges(); } catch (Exception ex) { Response.Write("出错了。信息:" + ex.ToString()); }
如图11-10所示为更新后的Books表内容。
图11-10 更新后的Books表内容
11.3.6 删除数据
如果要删除数据表中的数据,可以通过将表从对应的LINQ to SQL对象集合中删除来实现。此时,LINQ to SQL会将更改转换为相应的DELETE语句。
【范例19】
在11.3.5节的基础上对图书进行如下更改。
(1)删除编号为5的图书信息。
(2)删除所有价格为100的图书信息。
实现代码如下。
dbBooksDataContext db = new dbBooksDataContext(); //初始化dbBooksDataContext类 Books delBook = (from b in db.Books where b.Id == 5 select b).First(); db.Books.DeleteOnSubmit(delBook); //删除编号为5的图书信息 var delBooks = from b in db.Books where b.price == 100 select b; foreach(var b in delBooks){ db.Books.DeleteOnSubmit(b); //批量删除 } try{ db.SubmitChanges(); } catch (Exception ex){ Response.Write("出错了。信息:" + ex.ToString()); }
如图11-11所示为删除后的Books表内容。
图11-11 删除后的Books表内容
注意
LINQ to SQL不支持且无法识别级联删除操作。如果要在对行有约束的表中删除行,则必须在数据库的外键约束中设置ON DELETE CASCADE规则,或者使用自己的代码首先删除防止删除父对象的子对象,否则会引发异常。