浅谈.Net中数据保存的完整性

浅谈.Net中数据保存的完整性 收藏
保存DataSet和DataTable的方法大同小异,以下仅以保存DataTable为例。

作者:魏滔序

声明:转载请保留作者信息并注明出处。

在数据库中创建一个用来测试的表:


CREATE TABLE [dbo].[MyTable](

     [c1] [int] NOT NULL,

 CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED

(

     [c1] ASC

)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

 

设计模式:

 

 

查看模式:

 

 

通常情况下,我们保存一个Table的数据的方法如下:

Private Sub SaveData(ByVal Table As DataTable)

        Dim ConnectionText As String = "...代码略..."

        Dim SQL As String = "SELECT * FROM MyTable"

        Dim MyCn As OleDbConnection = New OleDbConnection(ConnectionText)

        Try

            Dim MyCmd As OleDbCommand = New OleDbCommand(SQL, MyCn)

            Dim MyDa As OleDbDataAdapter = New OleDbDataAdapter(MyCmd)

            Dim MyCb As OleDbCommandBuilder = New OleDbCommandBuilder(MyDa)

            MyDa.Update(Table)

        Catch ex As Exception

            MsgBox(ex.Message, 16, "错误")

        End Try

End Sub

 

这种方法看似没什么大问题,网络上的很多资料也如出一辙。

我们要求的是数据完整性,当在Table中添加了重复的记录并保存,由于C1列是主键,此时一定会抛出异常。

 

 

 

这个错误抛出的理所当然。看看数据库表中保存的结果,第一条成功保存了。

 

 

按照常理,我们此时要做的就是把第二条数据修正一下再保存即可完事大吉。但是如果此时用户退出了程序问题就出来了,数据库中仅保存了第一条记录,出现了“半拉子”工程,显然不是我们想看到的。那么,如果加入一个事务(Transaction)处理呢?利用他的回滚(Rollback)机制看似可以很好的解决这个问题。

代码:

Private Sub SaveData(ByVal Table As DataTable)

        Dim ConnectionText As String = "...代码略..."

        Dim SQL As String = "SELECT * FROM MyTable"

        Dim MyCn As OleDbConnection = New OleDbConnection(ConnectionText)

        Dim MyTran As OleDbTransaction

        MyCn.Open()

        MyTran = MyCn.BeginTransaction()

        Try

            Dim MyCmd As OleDbCommand = New OleDbCommand(SQL, MyCn)

            MyCmd.Transaction = MyTran

            Dim MyDa As OleDbDataAdapter = New OleDbDataAdapter(MyCmd)

            Dim MyCb As OleDbCommandBuilder = New OleDbCommandBuilder(MyDa)

           MyDa.Update(Table)

            MyTran.Commit()

       Catch ex As Exception

            MyTran.Rollback()

            MsgBox(ex.Message, 16, "错误")

        End Try

MyCn.Close()

    End Sub

 

清空数据库表中的数据,还是同样的测试方法,输入重复的值。结果抛出了和先前完全一样的异常(这句好像废话)。

 

 

这次数据表中果然没有数据被保存,基本上达到了我们的要求。

 

 

接下来我们把第二条数据修改为和第一条不同的数字再试试,比如修改为2,然后保存。程序的执行通过了。

 

 

我们再看看数据库表中的数据。

 

 

My God,竟然只保存了后来被修改的第二条记录。第一条数据哪里去了?怪哉!!

原来,在第一次执行MyDa.Update(Table)的时候,第一条数据是正常的,行状态在保存时被自动设置为Unchanged。但由于第二条出错,导致本该保存到数据库的第一条数据被回滚,这也没问题。问题就出在事务虽然回滚了,但被修改的行状态没有撤销,依然是修改后的Unchanged。当我们修改完第二条后再执行MyDa.Update(Table),非Unchanged状态的数据保存到了数据库,第一条就被无辜的遗漏了。

既然Table中的行状态会在保存的过程中被修改,那我们就先复制一个副本,对副本进行保存,副本的行状态爱怎么变就怎么变,我们不关心了(这个想法好像有点消极)。如果没问题则再对原始Table执行AcceptChanges。

代码:

    Private Sub SaveData(ByVal Table As DataTable)

        Dim ConnectionText As String = "...代码略..."

        Dim SQL As String = "SELECT * FROM MyTable"

        Dim MyCn As OleDbConnection = New OleDbConnection(ConnectionText)

        Dim MyTran As OleDbTransaction

        MyCn.Open()

        MyTran = MyCn.BeginTransaction()

        Try

            Dim MyCmd As OleDbCommand = New OleDbCommand(SQL, MyCn)

            MyCmd.Transaction = MyTran

            Dim MyDa As OleDbDataAdapter = New OleDbDataAdapter(MyCmd)

            Dim MyCb As OleDbCommandBuilder = New OleDbCommandBuilder(MyDa)

            Dim TabCopy As DataTable = Table.Copy

            MyDa.Update(TabCopy)

            MyTran.Commit()

            Table.AcceptChanges()

        Catch ex As Exception

            MyTran.Rollback()

            MsgBox(ex.Message, 16, "错误")

        End Try

MyCn.Close()

    End Sub

这里注意,复制Table要用DataTable的Copy函数,直接赋值并非真正的复制,另外DataTable的Clone函数出来的副本丢失了行状态,全部是Unchanged。

 

再测试一把,还是基本达到了我们的要求。但有两个不足,第一是内存资源稍微多耗一点点,第二是无法在DataGridView上标记出错误的行,就是前面的那个小红色图标。因为抛出异常的是DataGridView的DataSource的副本,跟DataGridView没有任何关系。

 

能不能做的更好呢?能!我们尝试从OleDbDataAdapter下手,粗略一看竟然发现了一个伟大的属性:AcceptChangesDuringUpdate,MSDN中是这样描述的:获取或设置在 System.Data.Common.DataAdapter.Update(System.Data.DataSet) 期间是否调用 System.Data.DataRow.AcceptChanges。

 

写代码继续测试之:

    Private Sub SaveData(ByVal Table As DataTable)

        Dim ConnectionText As String = "...代码略..."

        Dim SQL As String = "SELECT * FROM MyTable"

        Dim MyCn As OleDbConnection = New OleDbConnection(ConnectionText)

        Dim MyTran As OleDbTransaction

        MyCn.Open()

        MyTran = MyCn.BeginTransaction()

        Try

            Dim MyCmd As OleDbCommand = New OleDbCommand(SQL, MyCn)

            MyCmd.Transaction = MyTran

            Dim MyDa As OleDbDataAdapter = New OleDbDataAdapter(MyCmd)

            Dim MyCb As OleDbCommandBuilder = New OleDbCommandBuilder(MyDa)

            MyDa.AcceptChangesDuringUpdate = False

            MyDa.Update(Table)

            MyTran.Commit()

            Table.AcceptChanges()

        Catch ex As Exception

            MyTran.Rollback()

            MsgBox(ex.Message, 16, "错误")

        End Try

MyCn.Close()

    End Sub

测试结果是那么那么的优雅,那么那么的完美,大道至简也不过如此。(完)

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Modest/archive/2008/11/07/3245348.aspx

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值