2012-09-17 10 views
6

Dokonuję migracji klasycznej aplikacji dostępowej do serwera Sql, tj. Tabel połączonych DAO +.Transakcje korzystające z połączonych tabel DAO i Sql Server

Znalazłem zachowanie fustrujące: gdy dokonuję zmian za pomocą zestawów rekordów nad połączonymi tabelami, program Access używa więcej niż jednego połączenia. Więcej niż jedno połączenie oznacza więcej niż jedną transakcję na raz po stronie serwera. Te transakcje są niezależne. Nie zagnieżdżony.

Standardowe zachowanie MS Access przy użyciu tabel połączonych z plikami .mdb jest inne. Jest tylko jedna transakcja na czas. Każda zmiana db jest widoczna przez dowolny kod, który działa w tej samej przestrzeni roboczej DAO przed wykonaniem zatwierdzenia.

Reguły zostały zmienione, a istniejący kod DAO przy użyciu transakcji po stronie klienta zakończy się niepowodzeniem.

Jeśli dodaję lub aktualizuję rekord, używając zestawu rekordów otwartego jako dbOpenDynaset, kod próbujący odczytać je później nie powiedzie się: Nie znajduje nowych rekordów i nie wyświetla istniejących rekordów w stanie pierwotnym. Czemu? Ponieważ operacje są wycinane w wielu i niezależnych transakcjach Wykonując dostarczony przykładowy kod, program profilujący SQL pokaże, że wykonywane są różne operacje z różnymi identyfikatorami transakcji.

Testowałem to za pomocą ADO i wszystko działa dobrze. Ale są tysiące linii kodu.

Czy istnieje jakieś rozwiązanie inne niż przepisanie kodu za pomocą ADO?

Czy mogę zmodyfikować standardowe zachowanie dostępu? (użyj odczytu niezatwierdzonego poziomu izolacji, poinstruuj, aby nie otwierać nowych połączeń, ...)

Poniższy kod powtarza problem. To bardzo proste:

1.- Otwórz zestaw rekordów na istniejący rekord
2.- Dodaj nowy rekord
3.- Spróbuj przeczytać ostatnio dodanego rekordu

Jeśli użyję dbOpenDynaset w (1), i "nie zobaczę nowego rekordu w (3).

Używam ACC-2010, pliki w formacie Accdb i SQL Server 2008 R2

dzięki.

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 

Odpowiedz

2

Niestety, Microsoft twierdzi następujący temat Workspace.IsolateODBCTrans obiekt: http://msdn.microsoft.com/en-us/library/office/bb208483(v=office.12).aspx

Niektóre serwery ODBC, takie jak Microsoft SQL Server, nie pozwalają na jednoczesne transakcje w ramach jednego połączenia. Jeśli chcesz mieć więcej niż jedną transakcję oczekującą na taką bazę danych, ustaw właściwość IsolateODBCTrans na wartość True w każdym obszarze roboczym zaraz po jego otwarciu. Wymusza to oddzielne połączenie ODBC dla każdego obszaru roboczego.

Nie jestem pewien, czy to pomoże ci zdecydować, co robić.

+0

Dziękuję. Ale poddałem się. Zdecydowaliśmy się przepisać. "IsolateODBCTrans" nie rozwiąże problemu. Nie chcę więcej niż jednej transakcji, Just oine. Nie chcę więcej niż jednego połączenia. – ricardohzsz

+1

Tak też myślałem o problemie. Szkoda. Przynajmniej dostaniesz zmianę, aby zoptymalizować kod ;-) – milivojeviCH

+0

Jako jedyny, który próbował zaoferować rozwiązanie tego problemu, postanowiłem nagrodzić cię nagrodą;) Może to jest problem bez rozwiązania. Dzięki i tak. – Jonathan

0

Możesz nadal używać dao dla tabel, które pozostają w MBD. Jednak w przypadku tabel sqlserver (powiązanych tabel) w następujący sposób: Globalny objConn jako nowy ADODB.Połączenie

i rutyna:

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 

etc etc etc .....

+1

Nigdy nie używaj DoCmd.SetWarnings Fałszywego – Fionnuala

+0

@Fionnuala, ilekroć potrzebuję go używać, zawsze upewniam się, że piszę taki, który ustawia go "True" na końcu sub ... Czy to nie jest w porządku? – Ethan

Powiązane problemy