2012-09-17 35 views
6

我正在將一個Clasic Access應用程序遷移到Sql Server,即DAO + Linked表。使用DAO和Sql Server鏈接表的交易

我發現了一個fustrating行爲:當我使用記錄集對鏈接​​表進行更改時,Access使用多個連接。多個連接意味着服務器端的多個事務。這些交易是獨立的。不嵌套。

使用鏈接表到.mdb文件的標準MS訪問行爲是不同的。只有一次交易。在執行提交之前,每個數據庫更改都可以在同一個DAO.Workspace中運行的任何代碼顯示。

規則已更改,使用客戶端事務的現有DAO代碼將失敗。

如果我使用dbOpenDynaset打開的記錄集添加或更新記錄,試圖讀取它們後的任何代碼都將失敗:未找到新記錄並查看原始狀態下的現有記錄。爲什麼?因爲操作是在多個獨立事務中執行的。

執行提供的示例代碼時,sql事件探查器將向您顯示使用不同的事務ID進行的不同操作。

我已經測試了這個使用ADO,一切正常。但是有成千上萬的代碼行。

除了使用ADO重寫代碼之外,是否還有其他解決方案?

我可以修改標準訪問行爲嗎? (使用讀取未提交的隔離級別,指示不打開新的連接,...)

下面的代碼會重現該問題。這很簡單:

1.-打開一個記錄現有的記錄
2:添加新的記錄
3.-嘗試,如果我在使用dbOpenDynaset閱讀最近添加的記錄

(1),我(3)中不會看到新記錄。

我使用累積 - 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 

回答

2

不幸的是,微軟指出以下有關Workspace.IsolateODBCTrans物業: http://msdn.microsoft.com/en-us/library/office/bb208483(v=office.12).aspx

一些ODBC服務器,如微軟的SQL Server,不允許單個連接同時的交易。如果您需要一次處理多個事務,並且一次打開它,請將每個工作空間的IsolateODBCTrans屬性設置爲True。 這會爲每個工作區強制使用單獨的ODBC連接。

不知道這是否會幫助您決定要做什麼。

+0

謝謝。但我放棄了。我們決定重寫。 「IsolateODBCTrans」沒有解決。我不想要一個以上的交易,只是oine。我不想要多個連接。 – ricardohzsz

+1

這就是我對這個問題的看法。可憐。至少你得到一個改變來優化代碼有點;-) – milivojeviCH

+0

正如你誰試圖提供解決這一問題的唯一一個,我已經決定授予你的賞金;) 也許這是一個問題無解。不管怎麼說,還是要謝謝你。每當我需要使用它 – Jonathan

0

您可以繼續使用dao作爲保留在mdb中的那些表。但是對於像這樣的sqlserver表(鏈接表): 全局objConn作爲新的ADODB。連接

,並在常規:

Dim rst As ADODB.Recordset 
    DoCmd.SetWarnings False 
    If objConn.State <> adStateOpen Then 
     MsgBox ("Connection to SQL server has not been made. Please exit and resolve problem.") 
     Exit Sub 
    End If 

    Set rst = New ADODB.Recordset 

Dim stdocname As String 
rst.Open "tblbilling", objConn, adOpenDynamic, adLockPessimistic 

等等等.....

+1

切勿使用DoCmd.SetWarnings假 – Fionnuala

+0

@Fionnuala,我總是確保我寫一個,設置爲「真」處子結束......這是不正常? – Ethan