LINQ to SQL系列Part 7 - Updating our Database using Stored Procedures

   
LINQ to SQL系列Part 7 - Updating our Database using Stored Procedures

本文转载自:
http://www.cnblogs.com/hanxianlong/archive/2007/12/01/Updating-our-Database-using-Stored-Procedures.html

英文原贴链接:
http://weblogs.asp.net/scottgu/archive/2007/08/23/linq-to-sql-part-7-updating-our-database-using-stored-procedures.aspx

在第6部分我描述了如何用LINQ to SQL数据模型通过数据库的存储过程和用户自定义的函数来查询和检索数据。在今天的博客中我将讨论一下如何选择使用存储过程来更新、插入、删除数据。 为了帮助说明这些――让我们从为Northwind事例数据库建立一个数据访问层开始:

第一步:生成一个数据访问层(尚未用存储过程)

在第2部分(Part 2: Defining our Data Model Classes)的教程中,我讨论了如何使用VS2008中内嵌的Linq to SQL ORM设计器来生成如下的Linq to SQL类模型:


图1
 

向数据模型类中添加验证规则

 在定义数据模型类和关系之后,我们想向其中添加一些业务逻辑验证。我们可以通过向项目中添加一个部分类,在该类中向我们的数据模型类中添加验证规则(在第4部分Part 4: Updating our Database我讲述了如何来实现这种添加)

例如,我们可以添加一个业务规则来保证客户的电话号码是在一个合法的格式,并且在客户的要求运送的日期早于该订单的下单日期时不向数据库中添加该条记录。一旦在部分类中像如下这样添加了的话,这些验证方法将会在应用程序中更新数据模型时自动的触发。

VB:

图2
 

C#:


图3
 

 向DataContext中添加一个GetCustomer()帮助方法

既然我们生成了数据模型类,又向数据模型类中添加验证规则,那么我们就可以用查询数据和对数据进行交互。我们可以通过对我们的数据模型类写liqn 表达式并且将数据从中查询出来(在第3部分Part 3: Querying our Database我讲述了如何完成这一工作)。作为另外一种方式,我们可以将存储过程映射到我们的DataContext中并且使用他们来从数据模型类中查询数据(在第6部分Part 6: Retrieving Data using Stored Procedures的linq to sql中我讲述了如何完成这一工作)

 当建立linq to sql数据模型时,你经常想向你添加在DataContext的方法中压缩一些常用的LINQ to SQL查询(或者存储过程)。我们可以通过向项目中添加一个部分类来实现这一工作。使用,我们可以添加一个叫"GetCustomer()"的这样一个帮助方法来通过CustomerID的值从数据库中查询出一个Customer对象:

VB:

图4

C#:


图5
 

第二步:使用数据访问层(依然不使用存储过程)

 现在我们有了存入我们的数据模型的数据访问层,集成了业务验证规则,并且使得我们查询、更新、插入和删除数据

 让我们看一个使用它的简单场景,用它来查询出一个已经存在的客户对象,更新客户的ContactName和Phone Number,然后生成一个新的订单对象并且和新生成的ContactName和Phone Number关联起来。我们可以写如下的在单一的事务中执行的代码来完成这个工作,LINQ to SQL将会保证我们在对数据库做任何保存的时候自动触发业务逻辑验证规则。

VB:


图6
 

C#:


图7
 

LINQ to SQL管理我们对从数据库中查出的数据对象所做的更改,并且保持我们向数据模型中添加的对象的跟踪。当我们在最后调用DataContext.SubmitChanges()的方法的时候,LINQ to sQL将会检查我们的业务逻辑是否正确,如果正确的话,就会自动的生成动态sql语句来更新上面的Customer记录,并且向Orders表中添加一条新记录。


等一下――我觉得这个贴子是关于存储过程的

如果你还在读这个文章的话,你可能会感到很疑惑――哪儿有存储过程呢?为什么我展示了上面这些如何对数据模型对象写代码然后引发了动态sql来运行?为什么我还不展示如何调用存储过程来代替动态生成sql语句的方法来进行插入、更新、删除呢?

 原因是,在LINQ to SQL模型中的通过存储过程对数据模型类进行编程的编程模型是和通过动态生成SQL语句的编程模型是一样的。添加数据模型验证逻辑的方法是完全一样的(所以我们向数据模型类中添加的验证规则在我们使用存储过程之后依然是能够正常工作的)。我们用数据访问层来查询出一个客户,对它进行更新,然后添加一个和它相关联的订单的整个过程是一模一样的――不管我们是否用动态生成的sql来进行更新或者将我们的数据模型配置为用存储过程和代替动态生成的sql语句来进行更新。

 这种编程模型的对称性的的强大性包括两个方面:你不必去学两种做事的方法,还因为意味着你不必在项目开始的前期决定你是否使用存储过程。你开始可以用linq to sql关系模型动支持的动态生成sql语句来进行查询、插入、更新和删除。然后向模型中添加业务和验证规则。接着,你可以选择用存储过程来更新你的数据映射模型――或者不使用也可以。不管你是用动态sql语句还是用存储过程,你针对数据模型类写的代码和测试用例可以是保持不变的。

我们现在用本博客其他的部分来描述如何用已经配置好的存储过程对数据模型进行更新、插入和删除――而且我们依然用同样的验证规则,用上面同样的代码片断。


如何用存储过程来插入、更新、删除

我们修改一下我们已经建立好的数据访问层用存储过程替代动态生成sql来处理更新,用下面两种方法中的一种:

1)通过使用linq to sql 设计器用图形化的方式来配置存储过程,让它对我们对数据模型类做插入、更新、删除的操作时进行响应。

2)通过在项目中添加一个NorthwindDataContext部分类,然后通过实现其提供的在对数据模型对象进行插入、更新、删除时会调用的Insert/Update/Delete部分方法(例如InsertOrder,UpdateOrder,DeleteOrder)。这些部分方法会被传递到我们想更新的对象模型的实例中,然后我们就可以执行无论是存储过程还是sql语句。

当我们用方法1(linq to sql设计器)来以图形化的方式来配置调用存储过程时,它会生成跟你用第二种方法写出的一样的代码。总体来说,在90%的情况下,我推荐使用和1种方法――生成之后,在更高级的场景中,如果你需要的话,再对存储过程执行的代码做出微小的调整。

第3步:用存储过程插入订单 
 

我们用存储过程来对我们的数据模型进行转换,先对Order对象进行操作:

首先到VS的"Server Explorer"窗口,展开数据库的"Stored Procedures"节,然后右击它选择"Add New Stored Procedure":

图8
 

然后我们添加一条新的存储过程"InsertOrder",该存储过程向Orders表中插入一条新的Order记录:

图9
 

注意上面的存储过程是如何将"OrderID"参数定义为输出参数的。这是因为在数据库中的OrderID列是一个标识列,自增的字段。当调用该存储过程时,会传进一个NULL值,然后存储过程将会返回新插入的OrderID作为传出参数(通过在存储过程最后边调用SCOPE_INDENTITY()函数)。

生成存储过程之后,我们就打开linq to sql orm设计器。像我在本系列的的上篇博客( (Part 6: Retrieving Data Using Stored Procedures)中提到的,我们可以从server-explorer窗口中通过拖放的方式将存储过程拖到我们的DataContext设计器的方法面板上。现在我们要对新添加的InsertOrder存储过程进行这一步骤:

 

图10

最后一步是,告诉我们的数据访问层在向数据库中插入新的Order对象时使用InsertOrder存储过程。我们通过如下方式进行这一工作:选择"Order"类,打开其属性窗口点击"Insert"属性后的"…"按钮,来覆盖原有的当插入操作时调用的方法:

 

 图11

点击"…"按钮将会弹出一个如下的对话框,在该对话框中允许我们来自定义插入操作如何发生:

 

 图12

 注意上面的默认的模式("User Runtime")是如何让LINQ to SQL计算并且执行动态sql语句来处理插入操作的。我们可以通过选择"Customize"按钮,然后在可用的存储过程列表中选择我们的InsertOrder存储过程:

 

图13

 LINQ to sQL设计器将会列出我们的InsertOrder存储过程的参数列表。默认情况下,它会很智能的并且通过名称来试着选择"最合适"的选项。如果你乐意的话,你可以去覆盖它们。

 一旦我们点击了"ok"按钮,就完成了这一工作。现在,无论何时一个新的Order添加到我们的DataContext上并且SubmitChanges()方法被调用了,我们的InsertOrder存储过程将会替代动态的sql而执行。

重要:即使我们现在使用存储过程,刚才我们新建的自定义的的Order的"OnValidate()"的验证Order规则的部分方法(在本篇博客的第一步中创建的)依然会在任何的变动之前或者存储过程被执行的时候被触发。这意味着我们有一个比较清晰的方法在数据模型中添加上业务和验证规则,并且无论是用动态生成sql语句还是使用存储过程,都可以对它进行复用。


第4步:用存储过程进行自定义的更新


现在让我们修改一下Customer对象来用存储过程处理更新

 开始要创建一条如下的新的"UpdateCustomer"存储过程: 
 

图14
 

注意上面如何除了传入一个@CustomerID参数,们还传入了@Original_CustomerID参数。在Customer表中的中CustomerID列不是自增的字段,而且它可以在更新Customer对象的作为更新的一部分进行更新。因此,为了更新记录我们需要提供一个带有原始的CustomerID和新的CustomerID的存储过程。我们看一下如何使用linq to sql设计器来映射这个存储过程的。

你可以注意到上面我向该存储过程还传入了一个@Version参数(该参数是个时间戳)。这是我添加到Northwind的Customers表中的一个新列,用来帮助处理乐观并发冲突。我将会在本博客系列的后几篇更加深入的讲述一下乐观并发冲突――但是一个小的总结就是liqn to sql 完全支持并发冲突,并且使得你可以或者使用一个Version时间戳或者向你的存储过程提供原始值和新值的方法,来检测从你上面刷新了你的数据对象之后是否有其他的变化。在这个事例中我用的是时间戳,因为它便代码更整洁。

一旦我们生成了存储过程,我们可以拖/放它到linq to sql设计器,将它作为DataContext的一个方法。然后选择"Customer"类,点击在属性窗口中的"…"按钮来覆盖原有的Customer对象的Update行为。

 

图15

选择"Customize"单选按钮,选择使用我们的UpdateCustomer存储过程:

 
 图16
 

当将我们的Customer对象的属性映射到存储过程的参数时,你将会注意到在当对象首次被检索时,我们将选择将是传递数据对象的"当前"的属性值,还是在数据中的原始值。例如,我们想映射Customer.CustoerID的"当前"值到@CustomerID的存储过程和参数,还是我们将原有的值传递给"@original_customerID存储过程参数。

一旦我们点击了对话框的"ok"按钮,我们就完成了这一工作。现在无论何时当一个新的Customer被更新并且SubmitChanges()方法被调用时,我们的updateCustomer存储过程将会替代原有的动态生成的sql语句来执行。

重要:即使我们用了存储过程,在前面(本博客的第一步中)我们添加的Customer的包含了电话号码验证规则的"OnPhoneChanging()的部分方法依然会在将更改提交至数据库之前或者存储过程执行时执行。我们有一个比较清晰的方法在数据模型中添加上业务和验证规则,并且无论是用动态生成sql语句还是使用存储过程,都可以对它进行复用。


第5步:再次使用我们的数据访问层(这次使用存储过程)

一旦我们将数据访问层以存储过程替代动态生成sql语句,我们就可以重新运行在第2步写的针对我们的数据模型类写的一模一样的代码

 
 图17
 

现在,对Customer对象的更新,对Order对象的插入,都会执行存储过程而不是动态生成的sql语句。我们原先定义的验证规则依然执行,并且我们针对数据模型写的数据访问层的代码是一模一样的。

使用存储过程时的一些高级提示

在一些高级的存储过程场景下使用用linq to sql时你可能会觉得有用的一些提示:

存储过程的传出参数的使用:

在上面的插入场景(第3步)中,我展示了如何用存储过程的传出参数返回一个新的OrderID值(该值在Orders表中的一个自增的标识字段)。在使用存储过程和linq to sql一起工作时,你不仅仅是可以返回一个标识列,事实上你可以更改一下来返回任何的你的存储过程返回的参数。你可以在插入和更新的场景下使用这个方法。Linq to sQL将会把返回值带回并且使用来来更新你的数据模型的属性值,你不需要再次对数据库进行查询来更新、查询出它们来。

如果存储过程抛出错误的话将会发生什么?

如果一个存储过程在执行插入、更新、删除操作时抛出了一个异常,linq to sql 将会自动取消并且回滚所有和当前SubmitChanges()相关的所有事务变更。这保证了你的数据总是是保持清洁的、一致的状态。

你能用写代码的方式替代ORM设计器来调用存储过程吗?

如我刚才所说,你可以选择使用linq to sql orm设计器来将插入、更新、删除操作映射到存储过程,或者选择使用添加DataContext类的部分方法的方法来通过编程的方式来实现它。这儿是一个代码的例子,在NorthwindDataContext的部分类中写上如下代码来通过调用存储过程来覆盖掉UpdateCustomer行为。
 

图18
 

上面的代码实际上当我们用linq to sql orm设计器来映射存储过程并且将Update行为关联到Customer对象的时候,linq to sql设计器已经为我们生成好了。你可以在开始时使用它,然后添加任何其他的逻辑来使它更加高级一层(例如:使用存储过程的传回值在错误的情况下抛出自定义的错误,优化并发冲突等)。

总结

 Linq to SQL是一个非常灵活的对象关系模型。它使得你可以写出清晰的面向对象的代码来检索、更新和插入数据。

最好的是――它使得你可以清洁地不依赖于如何地存储和从数据库中如何加载来设计数据模型类。你可以用内置的ORM引擎通过动态生成的sql语句来对数据库中的数据进行有效的检索和更新。或者你可以配置你的数据层来用存储过程对其进行检索和更新。好的是,不用考虑使用的是动态sql语句还是存储过程,在数据层的代码和所有的逻辑验证规则都可以是一样的。

在接下来的本系列的博客中,我将讲述余下的Linq to SQL概念,包括:间表的继承,延迟/提早加载,优化并发冲突,下周我要休假,所以希望有一些自由的时间来写它们其中的一些知识。 
 

希望这些对你有所帮助

Scott

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值