处理以数据为中心的应用时,另一个重要的话题是事务管理。ado.net为事务管理提供了一个canada day非常干净和有效的api。因为ef运行在ado.net之上,所以ef可以使用ado.net的事务管理功能。
当从数据库角度谈论事务时,它意味着一系列操作被当作一个不可分割的操作。所有的操作要么全部成功,要么全部失败。事务的概念是一个可靠的工作单元,事务中的所有数据库操作应该被看作是一个工作单元。
从应用程序的角度来看,如果我们有多个数据库操作被当作一个工作单元,那么应该将这些操作包裹在一个事务否定之否定规律揭示了事物发展的中。为了能够使用事务,应用程序需要执行下面的步骤:
1、开始事务。
2、执行所有的查询,执行所有的数据库操作,这些操作被视为一个工作单元。
3、如果所有的事务成功了,那么提交事务。
4、如果任何一个操作失败,就回滚事务。
1、提到事务,最经典的例子莫过于银行转账了。我们这里也使用这个例子来理解一下和事务相关的概念。为了简单模拟银行转账的情景,假设银行为不同的账户使用了不同的表,对应地,我们创建了outputaccount和inputaccount两个实体类,实体类定义如下:
outputaccount实体类:
inputaccount实体类:
2、定义数据上下文类
3、使用数据迁移生成数据库,并填充种子数据
4、运行程序
从应用程序的角度看,无论何时用户将钱从outputaccount转入inputaccount,这个操作应该被视为一个工作单元,永远不应该发生outputaccount的金额扣除了,而inputaccount的金额没有增加。接下来我们就看一下ef如何管理事务。
运行程序前,先查看数据库数据:
现在,我们尝试使用ef的事务从outputaccount的张三转入1000给inputaccount的李四。
使用ef默认的事务执行
ef3岁幼儿早教训练的默认行为是:无论何时执行任何涉及create,update或delete的查询,都会默认创建事务。当dbcontext类上的savechanges()方法被调用时,事务就会提交。
运行程序后,会发现数据库中数据发生了改变:
可以看到,用户李四的账户上面多了1000,用户张三的账户上面少了1000。因此,这两个操作有效地被包裹在了一个事务当中,并作为一个工作单元执行。如果任何一个操作失败,数据就不会发生变化。
可能有人会疑惑:上面的程序执行成功了,没有看到事务的效果,能不能修改一下代码让上面的程序执行失败然后可以看到事务的效果呢?答案是肯定可以的,下面将上面的代码进行修改。
通过查看数据库表结构会发现balance的数据类型是
意味着balance列的最大可输入长度是16位(最大长度18位减去2位小数点),如果输入的长度大于16位的话程序就会报错,所以将上面的代码进行如下的修改:
在次运行程序,会发现程序报错了:
这时在查看数据库,发现用户张三的余额还是9000没有发生变化,说明事务起作用了。
5、使用transactionscop桌球杆架e处理事务
如果有一个场景具有多个dbcontext对象,那么我们想将涉及多个dbcontext对象的操作关联为一个工作单元,这时,我们需要在transactionscope对象内部包裹savechanges()方法的调用。为了描述这个场景,我们使用dbcontext类的两个不同实例来执行扣款和收款,代码如下:
在上面的代码中,我们使用了两个不同的dbcontext实例来执行扣款和收款操作。因此,默认的ef行为不会工作。在调用各自的savechanges()方法时,和上下文相关的各个事务不会提交。相反,因为它们都在transactionscope对象的内部,所以,当transactionscope对象的complete()方法调用时,事务才会提交。如果任何一个操作失败,就会发生异常,transactionscope就不会调用complete()方法,从而回滚更改。事务执行失败的案例也可以按照上面的方式进行修改,使balance列的长度超过最大长度,这里就不在演示了。
从ef6开始,ef在dbcontext对象上提供了databa.begintransaction()方法,当使用上下文类在事务中执行原生sql命令时,这个方法特别有用。
接下来看一下如何使用这个新方法管理事务。这里我们使用原生sql从outputaccount账户中扣款,使用模型类给inputaccount收款,代码如下:
对上面的代码稍作解释:首先创建了一个dbcontext类的实例,然后使用这个实例通过调用databa.begintransaction()方法开启了一个事务。该方法给我们返回了一个dbcontexttransaction对象的句柄,使用该句柄可以提交或者回滚事务。然后使用原生sql从outputaccount账户中扣款,使用模型类给inputaccount收款。调用savechanges()方法只会影响第二个操作(在事务提交之后影响),但不会提交事务。如果两个操作都成功了,那么就调用dbcontexttransaction对象的commit()方法,否则,我们就处理异常并调用dbcontexttransaction对象的rollback()方法回滚事务。
有时,我们想在ef的dbcontext类中使用一个已经存在的事务。原因可能有这么几个:
1、一些操作可能在应用的不同部分完成。
2、对老项目使用了ef,并且这个老项目使用了一个类库,这个类库给我们提供了事务或者数据库链接的句柄。
对于这些场景,ef允许我们在dbcontext类中使用一个和事务相关联的已存在连接。接下来,写一个简单的函数来模拟老项目的类库提供句柄,该函数使用纯粹的ado.net执行扣款操作,函数定义如下:
这种情况,我们不能使用databa.begintransaction()方法,因为我们需要将sqlconnection对象和sqltransaction对象传给该函数,并把该函数放到我们的事务里。这样,我们就需要首先创建一个sqlconnection,然后开始sqltransaction,代码如下:
同时,需要修改数据上下文类,数据库上下文类代码修改如下:
我们已经知道了好几种使用ef出来事务的方法,下面一一对号入座:
1、如果只有一个dbcontext类,那么应该尽力使用ef的默认事务管理。我们总应该将所有的操作组成一个在相同的dbcontext对象的作用域中执行的工作单元,savechanges()方法会提交处理事务。
2、如果使用了多个dbcontext对象,那么管理事务的最佳方法可能就是把调用放到transactionscope对象的作用域中了。
3、如果要执行原生的sql命令,并想把这些操作和事务关联起来,那么应该使用ef提供的databa.begintransaction()方法。然而这种方法只支持ef6以后的版本,以前的版本不支持。
4、如果想为要求sqltransaction的老项目使用ef,那么可以使用databa.utransaction()方法,在ef6中可用。
示例代码下载地址:点此下载
到此这篇关于entity framework使用code first模式管理事务的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持www.887551.com。
本文发布于:2023-04-06 01:19:12,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/28e53e347b832709f502152b3d08fdad.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:Entity Framework使用Code First模式管理事务.doc
本文 PDF 下载地址:Entity Framework使用Code First模式管理事务.pdf
留言与评论(共有 0 条评论) |