Author Topic: Transaction timing issues...  (Read 18724 times)

0 Members and 1 Guest are viewing this topic.

Stevray

  • Guest
Transaction timing issues...
« on: March 10, 2013, 02:38:21 PM »
I have a VB application that creates a large number of block references to a dynamic block very quickly.  Each block reference is created in it's own transaction.

It is my understanding that the transaction.commit functions does not actually commit the transaction; rather it queue the transaction to be committed.  Is that true?

If that is the case, is there a way of forcing the current thread to sleep until the transaction committal has been completed?  My app crashes after a pretty random number of  block reference creations and I think that the crash is caused because previous transactions have not completed.

If I follow the "transaction.commit" call with a call to "Threading.Thread.Sleep(0)", it delays the crash.  Without the sleep statement, my app crashes after the creation of a few to a few dozen block references.  With the sleep statement, the app happily creates a few hundred block references before it crashes.

Is there a way to force my app to sleep until the previously committed transaction has been completed?

Gasty

  • Newt
  • Posts: 90
Re: Transaction timing issues...
« Reply #1 on: March 10, 2013, 08:42:43 PM »
Hi,

Please show the code and error message, an insert routine shouldn't crash with a few insert (even with hundred of thousand), no matter the threading issue. Check if there are some objects that need to be disposed after each insert.

Gaston Nunez

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #2 on: March 11, 2013, 12:33:23 AM »
Amen to that,  I was trying to do some ridiculous testing and since the built in array command is limited to either 10 000 or 100 000 entities I had to make my own to insert 1 000 000 blocks.  It took an insanely long time (like half hour) but it worked.

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #3 on: March 11, 2013, 02:41:10 AM »
I have a VB application that creates a large number of block references to a dynamic block very quickly.  Each block reference is created in it's own transaction.

It is my understanding that the transaction.commit functions does not actually commit the transaction; rather it queue the transaction to be committed.  Is that true?

If that is the case, is there a way of forcing the current thread to sleep until the transaction committal has been completed?  My app crashes after a pretty random number of  block reference creations and I think that the crash is caused because previous transactions have not completed.

If I follow the "transaction.commit" call with a call to "Threading.Thread.Sleep(0)", it delays the crash.  Without the sleep statement, my app crashes after the creation of a few to a few dozen block references.  With the sleep statement, the app happily creates a few hundred block references before it crashes.

Is there a way to force my app to sleep until the previously committed transaction has been completed?

I doubt that the problem has anything to do with transactions, but you shouldn't need to use a separate transaction for every object, if this is happening in a registered command. What is the purpose of that, verses just doing everything in one transaction?

The most-common cause of 'indeterminate' crashes that happen at seemingly-random points after you make changes, open objects, etc., is that something was not deterministically disposed.

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #4 on: March 11, 2013, 10:12:13 AM »
Ok.  Here is the code and I have attached a very simple dwg file that has the dynamic block definition in it and pretty much nothing else.

Code: [Select]
' (C) Copyright 2013 by Don Stevens-Rayburn
'
Imports System
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.EditorInput

' This line is not mandatory, but improves loading performances
<Assembly: CommandClass(GetType(TestCode.TestClass))>

Namespace TestCode

    Public Class TestClass

        Private _parentBlockID As ObjectId
        Private _parentBlockName As String = "DynamicRectangle"

        <CommandMethod("Test")> Public Sub Test()
            Dim XbyX As XbyXEndView
            Dim point As New Point3d(0, 0, 0)
            Dim offset As New Vector3d(2.5, 0, 0)
            Dim num As Integer = 1
            Try
                Do While num <= 1000
                    AcadEditor.WriteLine("Generating block reference # " & num.ToString)
                    XbyX = New XbyXEndView
                    XbyX.Height = 5.5
                    XbyX.Width = 1.5
                    Dim acTransaction As Transaction = GetAutocadTransaction()
                    Using acTransaction
                        Dim modelSpace As BlockTableRecord
                        Dim bt As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
                        modelSpace = CType(acTransaction.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
                        XbyX.CreateReference(point, modelSpace)
                        XbyX.SetProperties()
                        acTransaction.Commit()
                    End Using
                    acTransaction.Dispose()
                    num += 1
                    point += offset
                Loop
            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                AcadEditor.ReportError(ex)
            End Try

        End Sub

        Public Shared Function GetAutocadTransaction() As Transaction
            Threading.Thread.Sleep(0)
            Dim tr As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
            tr.TransactionManager.QueueForGraphicsFlush()
            Threading.Thread.Sleep(0)
            Return tr
        End Function

        ' There had to be a better way of getting the objectId from the name!

    End Class

    Public Class XbyXEndView

        Private Shared _parentBlockID As ObjectId = Nothing
        Private _parentBlockName As String
        Private _acBlockReference As BlockReference = Nothing
        Private _layer As String = "<use current>"
        Private _rotation As Double
        Private _scaleX As Double = 1.0
        Private _scaleY As Double = 1.0
        Private _scaleZ As Double = 1.0

        Public Sub New()
            _parentBlockName = "XbyX End View"
            If _parentBlockID = Nothing Then
                _parentBlockID = GetParentBlockIdFromName(_parentBlockName)
            End If
        End Sub

        Public Property Width As Double = 1.5
        Public Property Height As Double = 3.5

        Public Sub SetProperties()
            AcadEditor.WriteLine("Attempting to set the properties of block: " & _acBlockReference.Name)
            SetProperty("Height", Height)
            SetProperty("Width", Width)
        End Sub

        '   There has to be a better way of doing this...
        Protected Function GetParentBlockIdFromName(blockName As String) As ObjectId
            Dim blockId As ObjectId = Nothing
            Dim acTransaction As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
            Using acTransaction
                Dim blockTable As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
                Dim bTIterator As SymbolTableEnumerator = blockTable.GetEnumerator
                If blockTable.Has(blockName) Then
                    Dim moreRecords As Boolean
                    Dim btr As BlockTableRecord
                    Dim btrID As ObjectId
                    bTIterator.Reset()
                    moreRecords = bTIterator.MoveNext
                    Dim success As Boolean = False
                    Do While moreRecords
                        btrID = bTIterator.Current
                        btr = CType(acTransaction.GetObject(btrID, OpenMode.ForRead), BlockTableRecord)
                        If btr.Name = blockName Then
                            blockId = btr.ObjectId
                            success = True
                            Exit Do
                        End If
                        moreRecords = bTIterator.MoveNext
                    Loop
                    If Not success Then
                        Throw New System.Exception("Look up failure for block: " & blockName)
                    End If
                Else
                    Throw New System.Exception("Block" & blockName & " does not exist in the current document...")
                End If
                acTransaction.Commit()
            End Using
            Return blockId
        End Function

        Public Sub CreateReference(Location As Point3d, blockTableRec As BlockTableRecord)
            If _acBlockReference = Nothing Then
                Dim transaction As Transaction = TestClass.GetAutocadTransaction()
                Using transaction
                    _acBlockReference = New BlockReference(Location, _parentBlockID)
                    Dim id As ObjectId = blockTableRec.AppendEntity(_acBlockReference)
                    If _acBlockReference.IsNewObject Then
                        transaction.AddNewlyCreatedDBObject(_acBlockReference, True)
                    End If
                    transaction.Commit()
                    _acBlockReference.ScaleFactors = New Scale3d(_scaleX, _scaleY, _scaleZ)
                    If _layer <> "<use current>" Then
                        _acBlockReference.Layer = _layer
                    End If
                    _acBlockReference.Rotation = _rotation
                End Using
                Threading.Thread.Sleep(50)
                transaction.Dispose()
            Else
                Throw New System.Exception("Reference already created...")
            End If
        End Sub

        Protected Sub SetProperty(ByVal Name As String, ByVal Value As Double)
            If _acBlockReference <> Nothing Then
                Threading.Thread.Sleep(0)
                Dim success As Boolean = False
                Dim propColl As DynamicBlockReferencePropertyCollection = _acBlockReference.DynamicBlockReferencePropertyCollection
                Dim prop As DynamicBlockReferenceProperty
                For Each prop In propColl
                    If prop.PropertyName.ToUpper = Name.ToUpper Then
                        prop.Value = Value
                        AcadEditor.WriteLine("Successfully set property " & Name & " on block reference: " & _acBlockReference.Name)
                        success = True
                        Exit For
                    End If
                Next
                If Not success Then
                    AcadEditor.WriteLine("Failed to set property " & Name & " on block reference: " & _acBlockReference.Name)
                    Throw New System.Exception(_parentBlockName & " does not have a property " & Name)
                End If
            Else
                Throw New System.Exception(_parentBlockName & " is not a dynamic block.")
            End If
        End Sub
    End Class

    Public Class AcadEditor

        Public Shared Sub ExecuteCommand(command As String)
            Application.DocumentManager.MdiActiveDocument.SendStringToExecute(Chr(27) & Chr(27) & command, True, False, True)
        End Sub

        Public Shared Sub WriteLine(ByVal message As String)
            Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbCrLf & message & vbCrLf)
        End Sub

        Public Shared Sub ReportError(ByVal ex As System.Exception)
            Dim message As String = ex.Message & vbCrLf & vbCrLf & ex.StackTrace
            message &= vbCrLf & vbCrLf & ex.Source
            If ex.InnerException IsNot Nothing Then
                message &= vbCrLf & vbCrLf & ex.InnerException.Message
            End If
            MsgBox(message, MsgBoxStyle.Critical, "Error")
        End Sub

    End Class
End Namespace

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #5 on: March 11, 2013, 03:14:38 PM »
A quick look at your code and I'm very confused about the implementation here...

First you're opening the block table up several times for each block inserted, I would wrap your main loop in a transaction, open the block table, and block table records just once and pass them along to the necessary segments of code.

Second, your xbyxEndView class doesn't appear to implement anything that a BlockReference already implements.  If you at least inherited your class from BlockReference then you might avoid redundant memory/processor use.

This leaves a lot of space for the issue TT is eluding to where you are not properly disposing objects.

What was the error message?
« Last Edit: March 11, 2013, 03:17:53 PM by WILL HATCH »

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #6 on: March 11, 2013, 04:46:00 PM »
OK, here is a somewhat simpler version of the code that uses only a single transaction to insert and configure each block.  It makes no sense to wrap the entire outer loop into a single transaction because this test code is only a small part of a much larger application.  That app needs to create varying number or x bx x's at various times, including, often, having to add a single instance of the x by x.  In addition, I am trying to build a framework that will allow me to manipulate a wide variety of dynamic blocks.

The block that I am using here is particularly simple; it has only height and width properties.  Again, in the test code they are all the same, but in the real application they change with almost every instance and so, replacing the x by x with a non-dynamic block does not solve the problem.

The error message that I get is one that I trap for on line 150 in the code; namely that the SetProperty method fails to find the property it is trying to set.  If that happened on the first instance of setting a group of properties, the debugging would be trivial.   What makes it harder, is that the code successfully sets the properties for several dozen instances of the block reference and then fails.  In, fact, it will often successfully set the Height property and then fail on its attempt to set the Width.

Code: [Select]
' (C) Copyright 2013 by Don Stevens-Rayburn
'
Imports System
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.EditorInput

' This line is not mandatory, but improves loading performances
<Assembly: CommandClass(GetType(TestCode.TestClass))>

Namespace TestCode

    Public Class TestClass

        Private _parentBlockID As ObjectId
        Private _parentBlockName As String = "DynamicRectangle"

        <CommandMethod("Test")> Public Sub Test()
            Dim XbyX As XbyXEndView
            Dim point As New Point3d(0, 0, 0)
            Dim offset As New Vector3d(2.5, 0, 0)
            Dim num As Integer = 1
            Try
                Do While num <= 1000
                    AcadEditor.WriteLine("Generating block reference # " & num.ToString)
                    XbyX = New XbyXEndView
                    XbyX.Height = 5.5
                    XbyX.Width = 1.5
                    Dim acTransaction As Transaction = GetAutocadTransaction()
                    Using acTransaction
                        Dim modelSpace As BlockTableRecord
                        Dim bt As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
                        modelSpace = CType(acTransaction.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
                        XbyX.CreateReference(point, modelSpace, acTransaction)
                        acTransaction.Commit()
                    End Using
                    acTransaction.Dispose()
                    num += 1
                    point += offset
                Loop
            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                AcadEditor.ReportError(ex)
            End Try

        End Sub

        Public Shared Function GetAutocadTransaction() As Transaction
            Threading.Thread.Sleep(0)
            Dim tr As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
            tr.TransactionManager.QueueForGraphicsFlush()
            Threading.Thread.Sleep(0)
            Return tr
        End Function

    End Class

    Public Class XbyXEndView

        Private Shared _parentBlockID As ObjectId = Nothing
        Private _parentBlockName As String
        Private _acBlockReference As BlockReference = Nothing
        Private _layer As String = "<use current>"
        Private _rotation As Double
        Private _scaleX As Double = 1.0
        Private _scaleY As Double = 1.0
        Private _scaleZ As Double = 1.0

        Public Sub New()
            _parentBlockName = "XbyX End View"
            If _parentBlockID = Nothing Then
                _parentBlockID = GetParentBlockIdFromName(_parentBlockName)
            End If
        End Sub

        Public Property Width As Double = 1.5
        Public Property Height As Double = 3.5

        '   There has to be a better way of doing this...
        Protected Function GetParentBlockIdFromName(blockName As String) As ObjectId
            Dim blockId As ObjectId = Nothing
            Dim acTransaction As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
            Using acTransaction
                Dim blockTable As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
                Dim bTIterator As SymbolTableEnumerator = blockTable.GetEnumerator
                If blockTable.Has(blockName) Then
                    Dim moreRecords As Boolean
                    Dim btr As BlockTableRecord
                    Dim btrID As ObjectId
                    bTIterator.Reset()
                    moreRecords = bTIterator.MoveNext
                    Dim success As Boolean = False
                    Do While moreRecords
                        btrID = bTIterator.Current
                        btr = CType(acTransaction.GetObject(btrID, OpenMode.ForRead), BlockTableRecord)
                        If btr.Name = blockName Then
                            blockId = btr.ObjectId
                            success = True
                            Exit Do
                        End If
                        moreRecords = bTIterator.MoveNext
                    Loop
                    If Not success Then
                        Throw New System.Exception("Look up failure for block: " & blockName)
                    End If
                Else
                    Throw New System.Exception("Block" & blockName & " does not exist in the current document...")
                End If
                acTransaction.Commit()
            End Using
            Return blockId
        End Function

        Public Sub CreateReference(Location As Point3d, blockTableRec As BlockTableRecord, acTransaction As Transaction)
            If _acBlockReference = Nothing Then
                 _acBlockReference = New BlockReference(Location, _parentBlockID)
                Dim id As ObjectId = blockTableRec.AppendEntity(_acBlockReference)
                If _acBlockReference.IsNewObject Then
                    acTransaction.AddNewlyCreatedDBObject(_acBlockReference, True)
                End If
                _acBlockReference.ScaleFactors = New Scale3d(_scaleX, _scaleY, _scaleZ)
                If _layer <> "<use current>" Then
                    _acBlockReference.Layer = _layer
                End If
                _acBlockReference.Rotation = _rotation
                SetProperty("Height", Height)
                SetProperty("Width", Width)
            Else
                Throw New System.Exception("Reference already created...")
            End If
        End Sub

        Protected Sub SetProperty(ByVal Name As String, ByVal Value As Double)
            If _acBlockReference <> Nothing Then
                Threading.Thread.Sleep(0)
                Dim success As Boolean = False
                Dim propColl As DynamicBlockReferencePropertyCollection = _acBlockReference.DynamicBlockReferencePropertyCollection
                Dim prop As DynamicBlockReferenceProperty
                For Each prop In propColl
                    If prop.PropertyName.ToUpper = Name.ToUpper Then
                        prop.Value = Value
                        AcadEditor.WriteLine("Successfully set property " & Name & " on block reference: " & _acBlockReference.Name)
                        success = True
                        Exit For
                    End If
                Next
                If Not success Then
                    AcadEditor.WriteLine("Failed to set property " & Name & " on block reference: " & _acBlockReference.Name)
                    Throw New System.Exception(_parentBlockName & " does not have a property " & Name)
                End If
            Else
                Throw New System.Exception(_parentBlockName & " is not a dynamic block.")
            End If
        End Sub
    End Class

    Public Class AcadEditor

        Public Shared Sub ExecuteCommand(command As String)
            Application.DocumentManager.MdiActiveDocument.SendStringToExecute(Chr(27) & Chr(27) & command, True, False, True)
        End Sub

        Public Shared Sub WriteLine(ByVal message As String)
            Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbCrLf & message & vbCrLf)
        End Sub

        Public Shared Sub ReportError(ByVal ex As System.Exception)
            Dim message As String = ex.Message & vbCrLf & vbCrLf & ex.StackTrace
            message &= vbCrLf & vbCrLf & ex.Source
            If ex.InnerException IsNot Nothing Then
                message &= vbCrLf & vbCrLf & ex.InnerException.Message
            End If
            MsgBox(message, MsgBoxStyle.Critical, "Error")
        End Sub

    End Class
End Namespace



TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #7 on: March 11, 2013, 05:50:07 PM »
OK, here is a somewhat simpler version of the code that uses only a single transaction to insert and configure each block.  It makes no sense to wrap the entire outer loop into a single transaction because this test code is only a small part of a much larger application.  That app needs to create varying number or x bx x's at various times, including, often, having to add a single instance of the x by x.  In addition, I am trying to build a framework that will allow me to manipulate a wide variety of dynamic blocks.

The block that I am using here is particularly simple; it has only height and width properties.  Again, in the test code they are all the same, but in the real application they change with almost every instance and so, replacing the x by x with a non-dynamic block does not solve the problem.

The error message that I get is one that I trap for on line 150 in the code; namely that the SetProperty method fails to find the property it is trying to set.  If that happened on the first instance of setting a group of properties, the debugging would be trivial.   What makes it harder, is that the code successfully sets the properties for several dozen instances of the block reference and then fails.  In, fact, it will often successfully set the Height property and then fail on its attempt to set the Width.

Code: [Select]

You should remove all calls to Thread.Sleep() because they could be causing the problem, and they are completely unnecessary.  In your sub that looks up the ObjectId of a BlockTableRecord, you only need to use the block table's indexer inside of a try/catch block to get the ObjectId of the block, there's no need for the IEnumerator business, if  you got that from one of Jerry Winter's books, throw the book in the trash.

I suspect the problem has something to do with your Sub that sets the property getting the DynamidBlockReferencePropertyCollection on each call. Refactor that sub to take the DynamicBlockReferencePropertyCollection as an argument, get it in the caller and pass it to the sub, so that the same instance of the collection is used to set all the properties. 

See if that works.
« Last Edit: March 11, 2013, 05:53:57 PM by TT »

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #8 on: March 11, 2013, 09:02:14 PM »
  • The thread.sleep calls were put in only to see if the problem was a simple timing issue.  If fact, having them delays the problem from occurring  (without the thread.sleep, the problem occurs within a dozen or so block reference creations; with them, it occurs within several dozen.
  • I have tried caching the dynamic block property collection when the block reference is first created and using that cached collection for the setting of the properties.  Does not have any effect (although it probably makes the code run a little faster.

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #9 on: March 11, 2013, 09:09:43 PM »
Missed your comment about getting the objectID of the blocktablerecord.  Can you give me a code snippet showing how to more efficiently get the objectID of a block given the name of the block.  As noted in the code, I said that there must be a better way than iterating through the entire blocktable but the AutoDesk documentation does not show one.

If you know of a similar "better way" of finding a specific dynamic block property given its name, I would love to see a code snippet.

Both of those methods are extremely kludgey.  One should not have to iterate though an entire collection to find a given item!

Thanks

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #10 on: March 12, 2013, 12:11:43 AM »
Missed your comment about getting the objectID of the blocktablerecord.  Can you give me a code snippet showing how to more efficiently get the objectID of a block given the name of the block.  As noted in the code, I said that there must be a better way than iterating through the entire blocktable but the AutoDesk documentation does not show one.

If you know of a similar "better way" of finding a specific dynamic block property given its name, I would love to see a code snippet.

Both of those methods are extremely kludgey.  One should not have to iterate though an entire collection to find a given item!

Thanks

Code - Visual Basic: [Select]
  1.  
  2.         Dim blockId As ObjectId = ObjectId.Null
  3.         Try
  4.             blockId = blockTable["blockname"]
  5.         Catch ex As System.Exception
  6.             ' Block was not found...
  7.        End Try
  8.  
  9.  
           

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #11 on: March 12, 2013, 12:38:19 AM »
  • The thread.sleep calls were put in only to see if the problem was a simple timing issue.  If fact, having them delays the problem from occurring  (without the thread.sleep, the problem occurs within a dozen or so block reference creations; with them, it occurs within several dozen.
  • I have tried caching the dynamic block property collection when the block reference is first created and using that cached collection for the setting of the properties.  Does not have any effect (although it probably makes the code run a little faster.

There are no timing-related issues in this case. The problem could be a bug in the API or in your code, but timing has nothing to do with it.

There's no need for your code to start and end a transaction for each BlockReference you add to the drawing, and to update the display after adding each using FlushGraphics.  What is the purpose of that?  You shouldn't need intermittent display updates unless there is going to be some kind of input requested from the user, and you're not doing that.

You can start a single transaction where the process begins, and use it throughout the process, by getting the TransactionManager's TopTransaction from wherever you need it. You can reach the TransactionManager through any database-resident DBObject, or ObjectId via their Database properties.

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #12 on: March 12, 2013, 01:34:15 AM »
That code actually has quite a few problems.

Here's one, though it shouldn't be causing the error:

Code - Visual Basic: [Select]
  1.  
  2. Using acTransaction
  3.    Dim modelSpace As BlockTableRecord
  4.    Dim bt As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
  5.    modelSpace = CType(acTransaction.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
  6.    XbyX.CreateReference(point, modelSpace, acTransaction)
  7.    acTransaction.Commit()
  8. End Using
  9. acTransaction.Dispose()    ; <------------ Look
  10.  
  11.  

Using disposes the object you give it, so you're disposing an object that has already been disposed.


WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #13 on: March 12, 2013, 02:23:07 AM »
It makes no sense to wrap the entire outer loop into a single transaction because this test code is only a small part of a much larger application.  That app needs to create varying number or x bx x's at various times, including, often, having to add a single instance of the x by x.  In addition, I am trying to build a framework that will allow me to manipulate a wide variety of dynamic blocks.


Considering the following modification to one of my existing block insertion routines

Code - C#: [Select]
  1.         [CommandMethod("DynamicBlockInsert")]
  2.         public void DynamicBlockInsert()
  3.         {
  4.             Document doc = Application.DocumentManager.MdiActiveDocument;
  5.             Editor ed = doc.Editor;
  6.             Database db = doc.Database;
  7.             PromptResult pr = ed.GetString("\nBlock to insert?");
  8.             if (pr.Status!=PromptStatus.OK) return;
  9.             string blockName = pr.StringResult;
  10.             using (Transaction tr = doc.TransactionManager.StartTransaction()) // starting the transaction out here allows for a single call to open block table et al
  11.             {
  12.                 BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  13.                 if (!bt.Has(blockName))
  14.                 {
  15.                     ed.WriteMessage("\nBlock {0} does not exist in the drawing", blockName);
  16.                     return;
  17.                 }
  18.                 BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
  19.                 ObjectId defId = bt[blockName];
  20.                 Point3d point = new Point3d(0, 0, 0);
  21.                 Vector3d offset = new Vector3d(2.5, 0, 0);
  22.                 Dictionary<string, double> PropertyValues = new Dictionary<string, double>();
  23.                 PropertyValues.Add("Width", 1.5);
  24.                 PropertyValues.Add("Height", 3.5);
  25.                 Dictionary<Point3d, Dictionary<string, double>> PointsAndProps = new Dictionary<Point3d, Dictionary<string, double>>();
  26.                 for (int i = 0; i < 1000; i++)
  27.                 {
  28.                     PointsAndProps.Add(point, PropertyValues);
  29.                     point = point.Add(offset);
  30.                 }
  31.                 DoInserts(ms, tr, defId, PointsAndProps, ed);
  32.                 tr.Commit();
  33.             }
  34.         }
  35.         private void DoInserts(BlockTableRecord ms,Transaction tr, ObjectId defId, Dictionary<Point3d,Dictionary<string,double>> PointsAndProps, Editor ed)
  36.         {
  37.             foreach (KeyValuePair<Point3d, Dictionary<string, double>> insertInfo in PointsAndProps)
  38.             {
  39.                 try
  40.                 {
  41.                     BlockReference br = new BlockReference(insertInfo.Key, defId);
  42.                     ms.AppendEntity(br);
  43.                     tr.AddNewlyCreatedDBObject(br, true);
  44.                     foreach (DynamicBlockReferenceProperty prop in br.DynamicBlockReferencePropertyCollection)
  45.                     {
  46.                         if (insertInfo.Value.Keys.Contains(prop.PropertyName))
  47.                             prop.Value = insertInfo.Value[prop.PropertyName];
  48.                     }
  49.                 }
  50.                 catch (System.Exception e)
  51.                 {
  52.                     ed.WriteMessage("\nFailed to insert block, i = {0}\n{1}", insertInfo.Key, e);
  53.                 }
  54.             }
  55.         }

I think you're overcomplicating the issue majorly.  You could even extend the method I've proposed here to work with different block def Ids etc to suit your purpose.

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #14 on: March 12, 2013, 02:29:29 AM »
It makes no sense to wrap the entire outer loop into a single transaction because this test code is only a small part of a much larger application.  That app needs to create varying number or x bx x's at various times, including, often, having to add a single instance of the x by x. 


Sorry, that's not true.

You can write code that assumes that there is an active transaction, and simply get it using the TransactionManager's TopTransaction property as I mention above.

The only thing you must do is to ensure that there is an active transaction when you call the code that makes that assumption, and that's it.

Usually, with code that is called from the handler of a registered command, the command handler starts and commits the single transaction, and any code called from it can then safely assume that there is an active transaction, and can get it from any Database-resident object or ObjectId.