我正在将一个传统的Access应用程序迁移到Sql Server,即DAO+链接表。我发现了一种令人沮丧的行为: 当我使用记录集对链接表进行更改时,Access会使用多个连接。多个连接意味着服务器端同时有多个事务。这些事务是独立的,不嵌套。
使用链接到.mdb文件的链接表的标准MS-Access行为是不同的。一次只有一个事务。在执行提交之前,任何运行在相同DAO.Workspace中的代码都可以看到每个数据库更改。
规则已经改变,使用客户端事务的现有DAO代码将失败。
如果我使用dbOpenDynaset打开作为记录集,则任何尝试在之后读取它们的代码都将失败:找不到新记录并且在原始状态下查看现有记录。为什么?因为操作是在多个独立事务中完成的。
执行提供的示例代码,SQL Profiler将显示不同的事务ID执行不同的操作。
我已经使用ADO测试过,并且一切都运行良好。但有成千上万行代码。
除了重写使用ADO的代码之外,是否有其他解决方案?
我可以修改标准访问行为吗?(使用“读未提交”隔离级别,指示不打开新连接等)
以下代码重现了问题。很简单:
1.打开现有记录的记录集 2.添加新记录 3.尝试读取最近添加的记录
如果我在(1)中使用dbOpenDynaset,则在(3)中将看不到新记录。
我正在使用Acc-2010、.accdb格式文件和Sql Server 2008 R2。谢谢。
使用链接到.mdb文件的链接表的标准MS-Access行为是不同的。一次只有一个事务。在执行提交之前,任何运行在相同DAO.Workspace中的代码都可以看到每个数据库更改。
规则已经改变,使用客户端事务的现有DAO代码将失败。
如果我使用dbOpenDynaset打开作为记录集,则任何尝试在之后读取它们的代码都将失败:找不到新记录并且在原始状态下查看现有记录。为什么?因为操作是在多个独立事务中完成的。
执行提供的示例代码,SQL Profiler将显示不同的事务ID执行不同的操作。
我已经使用ADO测试过,并且一切都运行良好。但有成千上万行代码。
除了重写使用ADO的代码之外,是否有其他解决方案?
我可以修改标准访问行为吗?(使用“读未提交”隔离级别,指示不打开新连接等)
以下代码重现了问题。很简单:
1.打开现有记录的记录集 2.添加新记录 3.尝试读取最近添加的记录
如果我在(1)中使用dbOpenDynaset,则在(3)中将看不到新记录。
我正在使用Acc-2010、.accdb格式文件和Sql Server 2008 R2。谢谢。
Private Sub test0()
Dim bResult As Boolean
Dim bUseTrans As Boolean 'New record added in transaction
Dim rsExist As DAO.Recordset2 'Dummy recordset
Dim tRecordsetExist As DAO.RecordsetTypeEnum 'Dummy recordset type:
' with dbOpenDynaset fail.
' Any other works fine
Dim rs2Add As DAO.Recordset
Dim rs2Read As DAO.Recordset 'Used to read recently added record
Dim tRecordset2Read As DAO.RecordsetTypeEnum 'Recordset type used to read new record. Doesn't affect
Dim bTranInitiated As Boolean 'Track if we are in transaction
Dim lngExistingNumber As Long
Dim lngNewNumber As Long
Dim lngNewID As Long
Dim strSQL As String
On Error GoTo HandleErr
'Invoices table definition in SS. Table is linked as [dbo_Invoices]:
' CREATE TABLE [dbo].[Invoices](
' [IdInvoice] [int] IDENTITY(1,1) NOT NULL,
' [InvoiceNumber] [int] NOT NULL,
' [InvoiceDescription] [varchar](50) NOT NULL,
' CONSTRAINT [PK_Invoices] PRIMARY KEY CLUSTERED
' (
' [IdInvoice] Asc
' )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
' ) ON [PRIMARY]
Set wks = DBEngine.Workspaces(0)
Set dbs = wks.Databases(0)
bUseTrans = True 'Without transaction everything works well
tRecordsetExist = dbOpenDynaset 'Dummy recordset type:
' dbOpenDynaset makes fail.
' Any other works fine
tRecordset2Read = dbOpenForwardOnly 'Does not affect
lngExistingNumber = 12001
lngNewNumber = -lngExistingNumber
'Clean previous runs of the test and make sure that referenced invoice exists.
dbs.Execute "Delete from dbo_Invoices Where InvoiceNumber = " & lngNewNumber, dbFailOnError Or dbSeeChanges
On Error Resume Next
strSQL = "Insert Into dbo_Invoices (InvoiceNumber, InvoiceDescription) " & _
" Values (" & lngExistingNumber & ", 'Original invoice' )"
dbs.Execute strSQL, dbFailOnError Or dbSeeChanges
On Error GoTo HandleErr
If bUseTrans Then
wks.BeginTrans
bTranInitiated = True
End If
strSQL = "Select IdInvoice, InvoiceNumber from dbo_Invoices " & _
" Where InvoiceNumber = " & lngExistingNumber
If tRecordsetExist = dbOpenDynaset Then
Set rsExist = dbs.OpenRecordset(strSQL, dbOpenDynaset, dbSeeChanges)
Else
Set rsExist = dbs.OpenRecordset(strSQL, tRecordsetExist)
End If
If rsExist.BOF And rsExist.EOF Then
Err.Raise vbObjectError, , "Original invoice " & lngExistingNumber & " not found"
End If
Set rs2Add = dbs.OpenRecordset("Select * from dbo_Invoices", dbOpenDynaset, dbAppendOnly Or dbSeeChanges)
rs2Add.AddNew
rs2Add!InvoiceNumber = lngNewNumber
rs2Add!InvoiceDescription = "Invoice anulation, ref " & lngExistingNumber
rs2Add.Update
'After executing .Update rs2Add goes to .EOF. This action reposition the recordset on the new record
rs2Add.Move 0, rs2Add.LastModified
lngNewID = rs2Add!IdInvoice
Debug.Print "New record added: IdInvoice = " & rs2Add!IdInvoice & ", InvoiceNumber = " & rs2Add!InvoiceNumber
'Try to read the new record
strSQL = "Select * from dbo_Invoices Where IdInvoice = " & lngNewID
If tRecordset2Read = dbOpenDynaset Then
Set rs2Read = dbs.OpenRecordset(strSQL, dbOpenDynaset, dbSeeChanges)
Else
Set rs2Read = dbs.OpenRecordset(strSQL, tRecordset2Read)
End If
If (rs2Read.BOF And rs2Read.EOF) Then
Err.Raise vbObjectError, , "rs2Read: Not found using IdInvoice = " & lngNewID
End If
Debug.Print "New record found with IdInvoice = " & rs2Read!IdInvoice
rs2Read.Close
bResult = True
ExitHere:
If Not wks Is Nothing Then
If bTranInitiated Then
If bResult Then
wks.CommitTrans
Else
wks.Rollback
End If
bTranInitiated = False
End If
End If
On Error Resume Next
If Not rs2Add Is Nothing Then
rs2Add.Close
Set rs2Add = Nothing
End If
If Not rs2Read Is Nothing Then
rs2Read.Close
Set rs2Read = Nothing
End If
Exit Sub
HandleErr:
Dim e As Object
If Err.Description Like "ODBC*" Then
For Each e In DBEngine.Errors
MsgBox e.Description, vbCritical
Next
Else
MsgBox Err.Description, vbCritical
End If
bResult = False
Resume ExitHere
Resume
End Sub