2012-09-17 33 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