Author Topic: Transaction timing issues...  (Read 18720 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.



Stevray

  • Guest
Re: Transaction timing issues...
« Reply #15 on: March 12, 2013, 08:25:46 AM »
I would like to thank everyone for their help.  I am a newbie at programming AutoCAD (although I have been writing computer code since 1964 and VB code since version 1.0).  Taking all of your comments into consideration, I have incorporated most of them into the test code shown below.  Unfortunately it still fails, this time at insertion #171.  After the code below is an error dump and I have attached a screen shot showing more of  the error details as well. 

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

        <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
            Dim acTransaction As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
            Using acTransaction
                Try
                    Dim acBlockTable As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
                    Dim acModelSpace As BlockTableRecord = CType(acTransaction.GetObject(acBlockTable(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
                    Do While num <= 1000
                        XbyX = New XbyXEndView(acBlockTable)
                        XbyX.Height = 5.5
                        XbyX.Width = 1.5
                        XbyX.CreateReference(point, acModelSpace, acTransaction)
                        num += 1
                        point += offset
                    Loop
                Catch ex As Autodesk.AutoCAD.Runtime.Exception
                    AcadEditor.ReportError(ex)
                End Try
            End Using
        End Sub

    End Class

    Public Class XbyXEndView

        Private Shared _parentBlockID As ObjectId = Nothing
        Private _parentBlockName As String = "XbyX End View"
        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
        Private _propertyInfo As New Dictionary(Of String, Double)

        Public Sub New(ByVal bt As BlockTable)
            If _parentBlockID = ObjectId.Null Then
                If bt.Has(_parentBlockName) Then
                    _parentBlockID = bt(_parentBlockName)
                End If
            End If
        End Sub

        Public Property Width As Double
            Get
                If _propertyInfo.ContainsKey("Width") Then
                    Return _propertyInfo("Width")
                Else
                    Return -1
                End If
            End Get
            Set(value As Double)
                If _propertyInfo.ContainsKey("Width") Then
                    _propertyInfo("Width") = value
                Else
                    _propertyInfo.Add("Width", value)
                End If
            End Set
        End Property

        Public Property Height As Double
            Get
                If _propertyInfo.ContainsKey("Height") Then
                    Return _propertyInfo("Height")
                Else
                    Return -1
                End If
            End Get
            Set(value As Double)
                If _propertyInfo.ContainsKey("Height") Then
                    _propertyInfo("Height") = value
                Else
                    _propertyInfo.Add("Height", value)
                End If
            End Set
        End Property

        Public Sub CreateReference(Location As Point3d, blockTableRec As BlockTableRecord, acTransaction As Transaction)
            If _acBlockReference = Nothing Then
                Try
                    _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
                    Dim propColl As DynamicBlockReferencePropertyCollection = _acBlockReference.DynamicBlockReferencePropertyCollection
                    For Each prop As DynamicBlockReferenceProperty In propColl
                        If _propertyInfo.ContainsKey(prop.PropertyName) Then
                            prop.Value = _propertyInfo(prop.PropertyName)
                        End If
                    Next
                Catch ex As System.Exception
                    Throw New System.Exception("Failed to insert block at " & Location.ToString, ex)
                End Try
            Else
                Throw New System.Exception("Reference already created...")
            End If
        End Sub

     End Class

    Public Class AcadEditor

        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

System.Exception: Failed to insert block at (427.5,0,0) ---> Autodesk.AutoCAD.Runtime.Exception: eNotApplicable
   at Autodesk.AutoCAD.DatabaseServices.DynamicBlockReferenceProperty.set_Value(Object Value)
   at TestCode.TestCode.XbyXEndView.CreateReference(Point3d Location, BlockTableRecord blockTableRec, Transaction acTransaction) in C:\Users\Don Stevens-Rayburn\Documents\Visual Studio 2010\Projects\TestCode\TestCode\TestClass.vb:line 113
   --- End of inner exception stack trace ---
   at TestCode.TestCode.XbyXEndView.CreateReference(Point3d Location, BlockTableRecord blockTableRec, Transaction acTransaction) in C:\Users\Don Stevens-Rayburn\Documents\Visual Studio 2010\Projects\TestCode\TestCode\TestClass.vb:line 117
   at TestCode.TestCode.TestClass.Test() in C:\Users\Don Stevens-Rayburn\Documents\Visual Studio 2010\Projects\TestCode\TestCode\TestClass.vb:line 31
   at Autodesk.AutoCAD.Runtime.CommandClass.InvokeWorker(MethodInfo mi, Object commandObject, Boolean bLispFunction)
   at Autodesk.AutoCAD.Runtime.CommandClass.InvokeWorkerWithExceptionFilter(MethodInfo mi, Object commandObject, Boolean bLispFunction)
   at Autodesk.AutoCAD.Runtime.PerDocumentCommandClass.Invoke(MethodInfo mi, Boolean bLispFunction)
   at Autodesk.AutoCAD.Runtime.CommandClass.CommandThunk.Invoke()


************** Loaded Assemblies **************
mscorlib
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.5466 (Win7SP1GDR.050727-5400)
    CodeBase: file:///C:/Windows/Microsoft.NET/Framework64/v2.0.50727/mscorlib.dll
----------------------------------------
acdbmgd
    Assembly Version: 18.0.0.0
    Win32 Version: 18.0.309.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/AcdbMgd.DLL
----------------------------------------
System
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.5467 (Win7SP1GDR.050727-5400)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
msvcm90
    Assembly Version: 9.0.30729.6161
    Win32 Version: 9.00.30729.6161
    CodeBase: file:///C:/Windows/WinSxS/amd64_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_08e61857a83bc251/msvcm90.dll
----------------------------------------
System.Drawing
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.5467 (Win7SP1GDR.050727-5400)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Drawing/2.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
System.Xml
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.5420 (Win7SP1.050727-5400)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Xml/2.0.0.0__b77a5c561934e089/System.Xml.dll
----------------------------------------
PresentationFramework
    Assembly Version: 3.0.0.0
    Win32 Version: 3.0.6920.5448 built by: Win7SP1GDR
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/PresentationFramework/3.0.0.0__31bf3856ad364e35/PresentationFramework.dll
----------------------------------------
WindowsBase
    Assembly Version: 3.0.0.0
    Win32 Version: 3.0.6920.5448 built by: Win7SP1GDR
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/WindowsBase/3.0.0.0__31bf3856ad364e35/WindowsBase.dll
----------------------------------------
PresentationCore
    Assembly Version: 3.0.0.0
    Win32 Version: 3.0.6920.5448 built by: Win7SP1GDR
    CodeBase: file:///C:/Windows/assembly/GAC_64/PresentationCore/3.0.0.0__31bf3856ad364e35/PresentationCore.dll
----------------------------------------
System.Configuration
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.5420 (Win7SP1.050727-5400)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Configuration/2.0.0.0__b03f5f7f11d50a3a/System.Configuration.dll
----------------------------------------
AdWindowsInterop
    Assembly Version: 0.0.0.0
    Win32 Version: 18.0.309.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/AdWindowsInterop.DLL
----------------------------------------
AdWindows
    Assembly Version: 2.1.0.0
    Win32 Version: 2.1.123.309
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/AdWindows.DLL
----------------------------------------
acmgd
    Assembly Version: 18.0.0.0
    Win32 Version: 18.0.309.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/acmgd.DLL
----------------------------------------
System.Core
    Assembly Version: 3.5.0.0
    Win32 Version: 3.5.30729.5420 built by: Win7SP1
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Core/3.5.0.0__b77a5c561934e089/System.Core.dll
----------------------------------------
AcWindows
    Assembly Version: 18.0.0.0
    Win32 Version: 18.0.309.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/AcWindows.DLL
----------------------------------------
AcWindows.resources
    Assembly Version: 18.0.0.0
    Win32 Version: 18.0.59.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/en-US/AcWindows.resources.DLL
----------------------------------------
AcCui
    Assembly Version: 18.0.0.0
    Win32 Version: 18.0.309.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/AcCui.DLL
----------------------------------------
System.Windows.Forms
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.5468 (Win7SP1GDR.050727-5400)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Windows.Forms/2.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
PresentationFramework.Aero
    Assembly Version: 3.0.0.0
    Win32 Version: 3.0.6920.4902 built by: NetFXw7
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/PresentationFramework.Aero/3.0.0.0__31bf3856ad364e35/PresentationFramework.Aero.dll
----------------------------------------
ContextualTabSelectorRules
    Assembly Version: 0.0.0.0
    Win32 Version: 18.0.309.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/AcWindows.dll
----------------------------------------
ManagedMC3
    Assembly Version: 2.20.0.0
    Win32 Version: 3.14.1
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/ManagedMC3.DLL
----------------------------------------
System.Web.Services
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.5420 (Win7SP1.050727-5400)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System.Web.Services/2.0.0.0__b03f5f7f11d50a3a/System.Web.Services.dll
----------------------------------------
AcLayer
    Assembly Version: 18.0.0.0
    Win32 Version: 18.0.309.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/AcLayer.DLL
----------------------------------------
AcLayer.resources
    Assembly Version: 18.0.0.0
    Win32 Version: 18.0.59.0.0
    CodeBase: file:///C:/Program%20Files/AutoCAD%202010/en-US/AcLayer.resources.DLL
----------------------------------------
WindowsFormsIntegration
    Assembly Version: 3.0.0.0
    Win32 Version: 3.0.6920.4902 built by: NetFXw7
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/WindowsFormsIntegration/3.0.0.0__31bf3856ad364e35/WindowsFormsIntegration.dll
----------------------------------------
bqzzon_n
    Assembly Version: 2.1.0.0
    Win32 Version: 2.0.50727.5467 (Win7SP1GDR.050727-5400)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
UIAutomationProvider
    Assembly Version: 3.0.0.0
    Win32 Version: 3.0.6920.4902 built by: NetFXw7
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/UIAutomationProvider/3.0.0.0__31bf3856ad364e35/UIAutomationProvider.dll
----------------------------------------
Accessibility
    Assembly Version: 2.0.0.0
    Win32 Version: 2.0.50727.4927 (NetFXspW7.050727-4900)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Accessibility/2.0.0.0__b03f5f7f11d50a3a/Accessibility.dll
----------------------------------------
TestCode
    Assembly Version: 1.0.0.0
    Win32 Version: 1.0.0.0
    CodeBase: file:///C:/Users/Don%20Stevens-Rayburn/Documents/Visual%20Studio%202010/Projects/TestCode/TestCode/bin/Debug/TestCode.dll
----------------------------------------
Microsoft.VisualBasic
    Assembly Version: 8.0.0.0
    Win32 Version: 8.0.50727.5420 (Win7SP1.050727-5400)
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualBasic/8.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualBasic.dll
----------------------------------------
Microsoft.VisualStudio.Debugger.Runtime
    Assembly Version: 10.0.0.0
    Win32 Version: 10.0.30319.1
    CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.Debugger.Runtime/10.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.Debugger.Runtime.dll
----------------------------------------

************** JIT Debugging **************
Application does not support Windows Forms just-in-time (JIT)
debugging. Contact the application author for more
information.



WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #16 on: March 12, 2013, 11:35:06 AM »
I'm just about stumped.  If you watch your ram usage while this operation is happening does it go over about 1GB for acad.exe during the course of the transaction (that's about what I saw in my version)

Also, not sure how this might impact http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx but in C# the default way to pass parameters into a subroutine is by ref, it appears vb.net is the opposite?

huiz

  • Swamp Rat
  • Posts: 917
  • Certified Prof C3D
Re: Transaction timing issues...
« Reply #17 on: March 12, 2013, 11:59:48 AM »
I'm just about stumped.  If you watch your ram usage while this operation is happening does it go over about 1GB for acad.exe during the course of the transaction (that's about what I saw in my version)

Also, not sure how this might impact http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx but in C# the default way to pass parameters into a subroutine is by ref, it appears vb.net is the opposite?

with vb.net the default is by value. Isn't it the same with c#? I thought in C# the default is by value too.
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transaction timing issues...
« Reply #18 on: March 12, 2013, 12:10:00 PM »
Looking quickly in your Test sub you start a transaction that will "blanket" all other transactions and it is never committed, so will roll back all changes made in nested transaction

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #19 on: March 12, 2013, 12:13:26 PM »
I'm just about stumped.  If you watch your ram usage while this operation is happening does it go over about 1GB for acad.exe during the course of the transaction (that's about what I saw in my version)

Also, not sure how this might impact http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx but in C# the default way to pass parameters into a subroutine is by ref, it appears vb.net is the opposite?

with vb.net the default is by value. Isn't it the same with c#? I thought in C# the default is by value too.

I was told it way by ref... After a look I think you're right... http://msdn.microsoft.com/en-us/library/0f66670z(v=vs.71).aspx
snap  :oops:

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #20 on: March 12, 2013, 12:14:44 PM »
VB definitely passes byVal by default.  I had always believed that C# did too.  I have not done much programming in C# but I know from my days programming C++ on VAXes that it generally passes by value.

Does your C# version successfully set the Height and Width of all 1000 instances.  In all of the failures that I have seen, it was attempting to set a property value when if failed even though the code had found a non-empty DynamicBlockReferencePropertyCollection.  The failure was always in the line

prop.value - whatever.

Sometimes it would even successfully set the height for the current instance and then fail to set the width.  That is what got me thinking that it might be a multithreading timing issue and why I threw all those thread.sleep calls into the code.

Like you, I am totally stumped.

And, yes, I forgot to commit the single transaction but the program fails long before it would get to a commit anyway.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transaction timing issues...
« Reply #21 on: March 12, 2013, 12:29:06 PM »
I'm just about stumped.  If you watch your ram usage while this operation is happening does it go over about 1GB for acad.exe during the course of the transaction (that's about what I saw in my version)

Also, not sure how this might impact http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx but in C# the default way to pass parameters into a subroutine is by ref, it appears vb.net is the opposite?

with vb.net the default is by value. Isn't it the same with c#? I thought in C# the default is by value too.
Default behavior is By Value.
Seems confusion comes from when the argument is a reference a type, which can contain only 2 values null or the address of an object.

By default, a copy of the contents held by the argument are used and if it is a reference type you have access to same the object, which I think is where confusion comes in.

One way to see the difference is to set the argument to null or to point to different object and see how only when passed by reference will that change the original variable.

You will see books and other material still say by default C# passes objects by reference.
« Last Edit: March 12, 2013, 12:32:32 PM by Jeff H »

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #22 on: March 12, 2013, 12:33:49 PM »
Yeah, mine did set all the properties.  I'm curious, could you break the dictionary call out of the property setting loop.  Maybe then we can determine what about the data is invalid, or if the dictionary call is the source of the problem.
Something like:
Code - vb.net: [Select]
  1.                     For Each prop As DynamicBlockReferenceProperty In propColl
  2.                         If _propertyInfo.ContainsKey(prop.PropertyName) Then
  3.                             Dim value As double
  4.                             try
  5.                                   value = _propertyInfo(prop.PropertyName)
  6.                             Catch ex As System.Exception
  7.                                   Editor.WriteMessage("Failed to get {0} from dictionary", prop.PropertyName)
  8.                             End Try
  9.                             try
  10.                                   prop.Value = value
  11.                             Catch ex As System.Exception
  12.                                   Editor.WriteMessage("Failed to set {0} value: {1}", prop.PropertyName, value)
  13.                             End Try
  14.                         End If
  15.                     Next
  16.  
« Last Edit: March 12, 2013, 12:38:27 PM by WILL HATCH »

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transaction timing issues...
« Reply #23 on: March 12, 2013, 01:04:59 PM »
Works for me if I commit, and I changed the number to 500 instead so I did not have to wait, but when it is not commited it gets aborted and has to go back and undo all changes made. Link with more info
Code - C#: [Select]
  1.         <CommandMethod("Test")> Public Sub Test()
  2.             Dim XbyX As XbyXEndView
  3.             Dim point As New Point3d(0, 0, 0)
  4.             Dim offset As New Vector3d(2.5, 0, 0)
  5.             Dim num As Integer = 1
  6.             Dim acTransaction As Transaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction()
  7.             Using acTransaction
  8.                 Try
  9.                     Dim acBlockTable As BlockTable = CType(acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead), BlockTable)
  10.                     Dim acModelSpace As BlockTableRecord = CType(acTransaction.GetObject(acBlockTable(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)
  11.                     Do While num <= 500
  12.                         XbyX = New XbyXEndView(acBlockTable)
  13.                         XbyX.Height = 5.5
  14.                         XbyX.Width = 1.5
  15.                         XbyX.CreateReference(point, acModelSpace, acTransaction)
  16.                         num += 1
  17.                         point += offset
  18.                     Loop
  19.                 Catch ex As Autodesk.AutoCAD.Runtime.Exception
  20.                     AcadEditor.ReportError(ex)
  21.                 End Try
  22. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  23. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  24.                 acTransaction.Commit()''''''''''''''''''''''''Commit
  25. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  26. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  27.             End Using
  28.         End Sub

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #24 on: March 12, 2013, 02:05:22 PM »
To Jeff H.

When I put the commit in it still fails randomly.  However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine.  However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?

What versions of acad, windows etc. are you running?

I am running Acad 2010 on a 64 bit quad-core Win7 machine.  Because I am running Acad 2010, i have to retarget the VB code from the Net Framework V4 to V3.5.

I would be interested to know whether you are running a newer version of AutoCad or are running a 32 bit version.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transaction timing issues...
« Reply #25 on: March 12, 2013, 02:32:22 PM »
Your width and height properties are set to list.
Are you using values defined in the list?
That was my first thought would your problem could be but it looked liked you were setting them to values in the list.


Tested on Win 7, AutoCAD 2013, 64bit

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #26 on: March 12, 2013, 11:08:46 PM »
To Jeff H.

When I put the commit in it still fails randomly.  However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine.  However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?

What versions of acad, windows etc. are you running?

I am running Acad 2010 on a 64 bit quad-core Win7 machine.  Because I am running Acad 2010, i have to retarget the VB code from the Net Framework V4 to V3.5.

I would be interested to know whether you are running a newer version of AutoCad or are running a 32 bit version.

I haven't tried your code, so this is just guessing.

Since the problem is obviously that the DynamicBlockReferenceProperty is throwing eNotApplicable when you try to set the value, you need to put in some more comprehensive debugging code, like for example, when the exception is caught, you should capture the value that you tried to set the property to, and any previous values that were successfully set, along with the values of ALL properties of the DynamicBlockReferenceProperty instance that threw the exception, and dump everything to the console. You can use TypeDescriptor.GetProperties() to get the PropertyDescriptors for all properties of the DynamicBlockReferenceProperty and use them to get the values of the properties and dump them to the console.

That of course, requires that you wrap the line that assigns the property value in a Try/Catch so that the Catch block has access to all local variables.

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #27 on: March 13, 2013, 11:36:46 AM »
Jeez, I haven't had this much fun since I spent 3 weeks pouring over a hex dump of memory trying to figure out why my program ABENDed with an error code of IBM911I.

For you youngsters, the made up word "ABEND" means "abnormally end".

To attempt to debug the abend code, one went to the IBM "Messages and Codes" manual which took an entire 3 foot shelf in the the key punch room.  If you have never had to enter a key punch room, thank the deity of your choice!

The entire entry in Messages and Codes for the error code IBM911I is "An error has occurred."

By the way, after spending those three weeks pouring over the memory dump, I never did find out why the program abended.  So, in absolute frustration, I ran it again and it worked just fine.  That's why I still remember the error code and description after thirty some years! Go figure!

At this point, I am giving up on the assumption that Autodesk's api has a bug in it and I will try to find a workaround.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #28 on: March 13, 2013, 01:31:10 PM »
awww
I'm still curious what the result of breaking out the property assignment in a try catch block would have been  :-( I used the 2010 dynamic block api a bit and didn't come across any errors like this.

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #29 on: March 13, 2013, 03:34:06 PM »
Dang!  I was hoping to put this behind me and get on with my life!

So, I added a try/catch block around the prop.value = whatever statement as in

Code: [Select]
   
                        Try
                                prop.Value = _propertyInfo(prop.PropertyName)
                            Catch ex As System.Exception
                                ExceptionDumper.Dump(prop, propColl, _propertyInfo, ex)
                                Throw New System.Exception("Failed to write dynamic block property value")
                            End Try

where ExceptionDumper is a purpose written bit of code that simply formats the data and dumps it to a file.  The result is

Code: [Select]
Dump of DynamicBlockReferenceCollection Follows

Name Height
Value 5.5
BlockID (8796088145280)
Description
Allowed Values System.Object[]
Type Code 1
ReadOnly False

Name Origin
Value (-0.000378755066321901,-0.00319671684420086,0)
BlockID (8796088145280)
Description
Allowed Values System.Object[]
Type Code 12
ReadOnly True

Name Width
Value 1.5
BlockID (8796088145280)
Description
Allowed Values System.Object[]
Type Code 1
ReadOnly False

Name Origin
Value (-0.000378755066321901,-0.00319671684420086,0)
BlockID (8796088145280)
Description
Allowed Values System.Object[]
Type Code 12
ReadOnly True

Dump of _propertyInfo Follows

Height 5.5
Width 1.5

Dump of the Top Level exception follows...

Message eAtMaxReaders
ErrorStatus AtMaxReaders
Source acdbmgd
TargetSite Void set_Value(System.Object)
StackTrace    at Autodesk.AutoCAD.DatabaseServices.DynamicBlockReferenceProperty.set_Value(Object Value)
   at TestCode.TestCode.XbyXEndView.CreateReference(Point3d Location, BlockTableRecord blockTableRec, Transaction acTransaction) in C:\Users\Don Stevens-Rayburn\Documents\Visual Studio 2010\Projects\TestCode\TestCode\TestClass.vb:line 117


So, the error code changes from eNotApplicable to eAtMaxReaders.  Anyone know what that means and how to get around it?

Frankly, AutoDesk's error handling sucks.  They might a well replace every error code with a simple "An error has occurred" and leave it at that.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transaction timing issues...
« Reply #30 on: March 13, 2013, 04:33:43 PM »
docs
Quote
If attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #31 on: March 13, 2013, 05:39:46 PM »
Thanks for doing that Stevray! My OCD is involved at this point and I'd like to learn the real issue here to avoid it in the future.  :ugly:


So, the error code changes from eNotApplicable to eAtMaxReaders.  Anyone know what that means and how to get around it?

Frankly, AutoDesk's error handling sucks.  They might a well replace every error code with a simple "An error has occurred" and leave it at that.
To Jeff H.

When I put the commit in it still fails randomly.  However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine.  However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?

After looking through your code for a nested item being explicitly opened and not finding one I came across your post where you had success by not setting the properties.

I hope somebody can confirm, but I'm guessing that each time we are accessing the block's property collection we are opening the definition for read.  Since in each iteration of the main loop we are creating a new instance of the XbyX_End_View class which we are not explicitly disposing then perhaps the definition is not being closed until the garbage collector cleans up?
If we explicitly dispose the object at the end of the main loop, or in the property setting loop we could possibly eliminate the problem???

What says you Jeff?
« Last Edit: March 13, 2013, 05:43:09 PM by WILL HATCH »

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transaction timing issues...
« Reply #32 on: March 13, 2013, 06:16:06 PM »
Thanks for doing that Stevray! My OCD is involved at this point and I'd like to learn the real issue here to avoid it in the future.  :ugly:


So, the error code changes from eNotApplicable to eAtMaxReaders.  Anyone know what that means and how to get around it?

Frankly, AutoDesk's error handling sucks.  They might a well replace every error code with a simple "An error has occurred" and leave it at that.
To Jeff H.

When I put the commit in it still fails randomly.  However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine. However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?

After looking through your code for a nested item being explicitly opened and not finding one I came across your post where you had success by not setting the properties.

I hope somebody can confirm, but I'm guessing that each time we are accessing the block's property collection we are opening the definition for read.  Since in each iteration of the main loop we are creating a new instance of the XbyX_End_View class which we are not explicitly disposing then perhaps the definition is not being closed until the garbage collector cleans up?
If we explicitly dispose the object at the end of the main loop, or in the property setting loop we could possibly eliminate the problem???

What says you Jeff?


I missed that or skimmed over it to quickly,


Did you catch reply
Your width and height properties are set to list.
Are you using values defined in the list?
That was my first thought would your problem could be but it looked liked you were setting them to values in the list.


Tested on Win 7, AutoCAD 2013, 64bit


Look at picture below or open your drawing and select a BlockReference then on the properties palette notice how the Width and Height properties or limited to what's in the combobox.
Does code work when using any of those values or change from list to none


As a side question do you work for a envelope company?


WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #33 on: March 13, 2013, 06:51:15 PM »
As a side question do you work for a envelope company?

 :lmao:

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #34 on: March 14, 2013, 02:37:55 AM »
eAtMaxReaders clearly suggests to me, a problem in the API somewhere, and I wouldn't doubt it, given that (in earlier releases at least), a BlockReference had to be transaction-resident to access its dynamic block properties (don't know if that's still the case, but if not and it was 'fixed', then I think this is a side-effect of the 'fix').

The strange thing is that this should never happen if you use a separate transaction for inserting each block reference, and there is no outer Transaction enclosing everything. Any objects that are being opened should be closed when the outermost transaction is committed.

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #35 on: March 14, 2013, 06:10:48 AM »
An envelope company?  An envelope company?  :lmao:  Why would you think that I work for an envelope company?

I am using values for height and width that are defined in the list of allowed values.  If you have done any building at all, you would recognize the allowed values as the true size of a 2x4, 2x6, ... 4x4, ...  In other words, the dynamic block is the end view of a piece of wood.

I think that we are all now agreed that, to misquote Shakespeare, the problem is not in our stars but is in the api! 

The only outstanding question is how to work around it.  If you really can only set the value of a dynamic block property 256 times that really limits their usefulness. Can anybody think of a way around this limit.


Stevray

  • Guest
Re: Transaction timing issues...
« Reply #36 on: March 14, 2013, 07:56:32 AM »
docs
Quote
If attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.

Does not the END USING statement close all  references to the db that were opened withing the scope of the transaction?

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #37 on: March 14, 2013, 09:04:23 AM »
yes, but only the top transaction.  The routine is crashing before we get there.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #38 on: March 14, 2013, 09:12:59 AM »
As a workaround... The vertical products I've worked with were wrapping my instructions in a transaction so it would probably need to start by committing and disposing of the top transaction, then break the workload up into smaller pieces each wrapped in their own transaction. Sounds messy... Thanks AutoDesk!

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #39 on: March 14, 2013, 04:32:23 PM »
docs
Quote
If attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.

Does not the END USING statement close all  references to the db that were opened withing the scope of the transaction?

Yes, for the outer-most transaction.

I can't run your code, but here are a few things I would try:  First, try changing StartTransaction() to StartOpenCloseTransaction(). You can also try changing your CommandMethod to a LispFunction with a name starting with "C:", and adding a ResultBuffer argument (which isn't used).  ObjectARX commands are implicitly undo groups and may also have some affect on how transactions behave. As far as I can tell your code isn't opening any object > 255 times, so it must be an object being opened internally by the API that is breaching the limit.

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #40 on: March 14, 2013, 05:09:53 PM »
yes, but only the top transaction.  The routine is crashing before we get there.

Even if I delete the outer most transaction and wrap each block reference insertion in it's own transaction, it still crashes.    :cry:

Gasty

  • Newt
  • Posts: 90
Re: Transaction timing issues...
« Reply #41 on: March 14, 2013, 06:46:18 PM »
Hi,

This is weird, I'd tried different approaches to this, using just a transaction, 1 top transaction plus a local insert transaction, and in both cases AutoCAD go south after a random number of inserts (over 150), in some cases with "funny" results like a message about a proxy in the block definition, no erasable inserts, etc, obviously because a memory or db corruption . After some test I have a code that at least does not crash AutoCAD (tested up to 1501 inserts), but have a serious performance problem,  that I routed to the change of dynprops part, the time it takes to change the values is consistently increasing  (up to seconds) after each insert, but I don't see a memory issue in process explorer, just a delay changing the dynprops. The code is crude, assuming a lot of things with a minimum error checking :

Code: [Select]
    Public Sub InsertBlockTest3(ByVal blkName As String, ByVal px As Point3d, ByVal Theta As Double, ByVal sx As Double, ByVal sy As Double, ByVal sz As Double, ByVal Height As Double, ByVal Width As Double, ByVal Layer As String)
        Dim Db As Database

        Db = HostApplicationServices.WorkingDatabase()

        Using acTrans As Transaction = Db.TransactionManager.StartTransaction()
            Dim bt As BlockTable = acTrans.GetObject(Db.BlockTableId, OpenMode.ForRead)
            Dim btr As BlockTableRecord
            Dim id As ObjectId

            btr = acTrans.GetObject(bt.Item(BlockTableRecord.ModelSpace), OpenMode.ForWrite)

            If bt.Has(blkName) Then
                Dim btrSrc As BlockTableRecord
                btrSrc = acTrans.GetObject(bt.Item(blkName), OpenMode.ForRead)
                id = btrSrc.Id
            Else
                MsgBox("Block definition not found for Block:" + blkName)

            End If

            Dim blkRef As New BlockReference(px, id)
            Dim sc As New Scale3d(sx, sy, sz)
            blkRef.Layer = Layer
            blkRef.Rotation = Theta
            blkRef.ScaleFactors = sc
            btr.AppendEntity(blkRef)
            acTrans.AddNewlyCreatedDBObject(blkRef, True)

            Dim dPropsCollection As DynamicBlockReferencePropertyCollection

            dPropsCollection = blkRef.DynamicBlockReferencePropertyCollection

            Dim t1 As Date = Now
            For Each dprop As DynamicBlockReferenceProperty In dPropsCollection
                Select Case dprop.PropertyName
                    Case "Heigth"
                        dprop.Value = Height
                    Case "Width"
                        dprop.Value = Width
                End Select
            Next

            Dim t2 As Date = Now
            Debug.Print("Time in props:" + (t2 - t1).TotalMilliseconds.ToString + "[ms]")

            Try
                btr.UpdateAnonymousBlocks()
                blkRef.RecordGraphicsModified(True)
            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                MsgBox("Error Updating RefBTR:blkName :=" + blkName + ": " + ex.Message)
            End Try
            acTrans.Commit()
        End Using
    End Sub

    <CommandMethod("BTest3")> _
    Public Sub btest3()
        Dim blkName As String = "A"
        Dim px As New Point3d(0, 0, 0)
        Dim offset As New Vector3d(4.5, 0, 0)
        Debug.Print(Now.TimeOfDay.ToString)
        For i = 0 To 199
            InsertBlockTest3(blkName, px, 0, 1, 1, 1, 2, 4, "0")
            px += offset
        Next
        Debug.Print(Now.TimeOfDay.ToString)
    End Sub

My concerns are not only with the poor documentation, but with the absence of a clear workflow and design patterns to achieve a very recurrent task as insert blocks. With AutoCAD 2013 we are about to complete 9 version of this API without a decent documentation, clear design patterns and official guidance.

Regards,

Gaston Nunez


Jeff H

  • Needs a day job
  • Posts: 6150
Re: Transaction timing issues...
« Reply #42 on: March 14, 2013, 08:49:13 PM »
Has anyone with problems crashing checked the AnonymousBlockTableRecord & DynamicBlockTableRecord properties before and after changing its properties?

LE3

  • Guest
Re: Transaction timing issues...
« Reply #43 on: March 14, 2013, 09:19:05 PM »
maybe i have not read all, but i can do an update of a an dynamic block property without an error, just tested with a simple stud block (rectangle) copied on the drawing 2500+ times, that in my case has a "Width" property name. See if helps...

Code - C#: [Select]
  1.         [CommandMethod("UPDYNBLKS")]
  2.         public void cmd_updateDynamicBlocks()
  3.         {
  4.             Editor e = AcadApp.DocumentManager.MdiActiveDocument.Editor;
  5.             TypedValue[] tv = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") };
  6.             SelectionFilter filter = new SelectionFilter(tv);
  7.             PromptSelectionOptions options = new PromptSelectionOptions();
  8.             options.MessageForAdding = "\nAdd blocks to selection";
  9.             options.MessageForRemoval = "\nRemove blocks from selection";
  10.             options.AllowDuplicates = false;
  11.             options.RejectObjectsFromNonCurrentSpace = true;
  12.             PromptSelectionResult psr = e.GetSelection(options, filter);
  13.             if (psr.Status != PromptStatus.OK) return;
  14.             string blockName = "STUD";
  15.             string propertyName = "Width";
  16.             double wValue = 4.0;
  17.             using (var tr = e.Document.Database.TransactionManager.StartTransaction())
  18.             {
  19.                 var blockTable = tr.GetObject(e.Document.Database.BlockTableId, OpenMode.ForRead) as BlockTable;
  20.                 var blockTableRecord = tr.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
  21.                 foreach (var entityId in blockTableRecord)
  22.                 {
  23.                     var dynamicBlock = tr.GetObject(entityId, OpenMode.ForWrite, false) as BlockReference;
  24.                     if (dynamicBlock != null && !dynamicBlock.IsErased && string.Compare(dynamicBlock.Name, blockName, true) == 0 && dynamicBlock.IsDynamicBlock)
  25.                     {
  26.                         dynamicBlock.DynamicBlockReferencePropertyCollection.Cast<DynamicBlockReferenceProperty>().ToList().ForEach(dynamicProperty =>
  27.                         {
  28.                             try
  29.                             {
  30.                                 if (!dynamicProperty.ReadOnly && propertyName == dynamicProperty.PropertyName)
  31.                                 {
  32.                                     if (dynamicProperty.PropertyTypeCode == 1)
  33.                                     {
  34.                                         dynamicProperty.Value = wValue;
  35.                                     }
  36.                                 }
  37.                             }
  38.                             catch (System.Exception ex)
  39.                             {
  40.                                 e.WriteMessage("\nError: {0} \n", ex.Message);
  41.                             }
  42.                         });
  43.                     }
  44.                 }
  45.                 tr.Commit();
  46.             }
  47.         }
  48.  

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #44 on: March 15, 2013, 01:36:00 AM »
Has anyone with problems crashing checked the AnonymousBlockTableRecord & DynamicBlockTableRecord properties before and after changing its properties?

The performance issues with scripting dynamic blocks and their properties has existed from the beginning, and it has to do with the failure to expose APIs that allow the programmer to inform the Dynamic block API that they are going to be make many changes to many properties, and/or many dynamic block insertions, and the failure of the API to be able to apply those changes without a 'recalcl' (so to speak) happening on each and every one. In other words, the issue doesn't exist insofar as interactive editing of Dynamic blocks, because the user doesn't perform the operation in mass, except perhaps, when they've selected many insertions and use the Properties Palette to change a property common to all selected inserts.

Every time you change a dynamic block property, it requires AutoCAD to first check to see if there are any existing Anonymous blocks whose properties are all the same as the dynamic block reference that you've changed, and if it finds one that matches, it will then set the block reference to that anonymous block. That's if you're lucky. Otherwise, a new anonymous block must be defined and the insertion must be set to reference that. So, there's quite a bit of things happening under the covers when you do something seemingly simple, like change a dynamic block property.

Implementing features that are unquestionably scripting-unfriendly is something we are seeing more and more of from Autodesk (FLATSHOT, Solid 3d primitives, etc).

I think the only way to get around the problem is to insert with default properties, commit, and then return and open each insertion and modify their properties.

LE3

  • Guest
Re: Transaction timing issues...
« Reply #45 on: March 16, 2013, 07:12:59 PM »
was able to play a little more, using your code but converted to c# in one of the online tools on the net, did some minor changes, and all tests runs with no error.

Code - C#: [Select]
  1.     public class TestClass
  2.     {
  3.  
  4.         private ObjectId _parentBlockID;
  5.  
  6.         //private string _parentBlockName = "DynamicRectangle";
  7.         [CommandMethod("Tester1")]
  8.         public void Test_swamp()
  9.         {
  10.             XbyXEndView XbyX = null;
  11.             Point3d point = new Point3d(0, 0, 0);
  12.             Vector3d offset = new Vector3d(2.5, 0, 0);
  13.             int num = 1;
  14.             try
  15.             {
  16.                 while (num <= 1000)
  17.                 {
  18.                     //AcadEditor.WriteLine("Generating block reference # " + num.ToString());
  19.                     XbyX = new XbyXEndView();
  20.                     XbyX.Height = 5.5;
  21.                     XbyX.Width = 1.5;
  22.                     Transaction acTransaction = GetAutocadTransaction();
  23.                     using (acTransaction)
  24.                     {
  25.                         BlockTableRecord modelSpace = default(BlockTableRecord);
  26.                         BlockTable bt = (BlockTable)acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead);
  27.                         modelSpace = (BlockTableRecord)acTransaction.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
  28.                         XbyX.CreateReference(point, modelSpace);
  29.                         XbyX.SetProperties();
  30.                         acTransaction.Commit();
  31.                     }
  32.                     //acTransaction.Dispose();
  33.                     num += 1;
  34.                     point += offset;
  35.                 }
  36.             }
  37.             catch (Autodesk.AutoCAD.Runtime.Exception ex)
  38.             {
  39.                 //AcadEditor.ReportError(ex);
  40.             }
  41.  
  42.         }
  43.  
  44.         public static Transaction GetAutocadTransaction()
  45.         {
  46.             //System.Threading.Thread.Sleep(0);
  47.             Transaction tr = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction();
  48.             //tr.TransactionManager.QueueForGraphicsFlush();
  49.             //System.Threading.Thread.Sleep(0);
  50.             return tr;
  51.         }
  52.  
  53.         // There had to be a better way of getting the objectId from the name!
  54.  
  55.     }
  56.  
  57.     public class XbyXEndView
  58.     {
  59.  
  60.         private static ObjectId _parentBlockID = ObjectId.Null;
  61.         private string _parentBlockName;
  62.         private BlockReference _acBlockReference = null;
  63.         private string _layer = "<use current>";
  64.         private double _rotation = 0.0;
  65.         private double _scaleX = 1.0;
  66.         private double _scaleY = 1.0;
  67.  
  68.         private double _scaleZ = 1.0;
  69.         public XbyXEndView()
  70.         {
  71.             _parentBlockName = "XbyX";// End View";
  72.             if (_parentBlockID == ObjectId.Null)
  73.             {
  74.                 _parentBlockID = GetParentBlockIdFromName(_parentBlockName);
  75.             }
  76.         }
  77.  
  78.         public double Width { get; set; }
  79.         public double Height { get; set; }
  80.  
  81.         public void SetProperties()
  82.         {
  83.             //AcadEditor.WriteLine("Attempting to set the properties of block: " + _acBlockReference.Name);
  84.             SetProperty("Height", Height);
  85.             SetProperty("Width", Width);
  86.         }
  87.  
  88.         //   There has to be a better way of doing this...
  89.         protected ObjectId GetParentBlockIdFromName(string blockName)
  90.         {
  91.             ObjectId blockId = ObjectId.Null;
  92.             Editor e = AcadApp.DocumentManager.MdiActiveDocument.Editor;
  93.             using (var tr = e.Document.Database.TransactionManager.StartTransaction())
  94.             {
  95.                 var blockTable = tr.GetObject(e.Document.Database.BlockTableId, OpenMode.ForRead) as BlockTable;
  96.                 var blockTableRecord = tr.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
  97.                 foreach (var entityId in blockTableRecord)
  98.                 {
  99.                     var dynamicBlock = tr.GetObject(entityId, OpenMode.ForWrite, false) as BlockReference;
  100.                     if (dynamicBlock != null && !dynamicBlock.IsErased && string.Compare(dynamicBlock.Name, blockName, true) == 0 && dynamicBlock.IsDynamicBlock)
  101.                     {
  102.                         blockId = new ObjectId(new IntPtr(dynamicBlock.DynamicBlockTableRecord.OldId));
  103.                         break;
  104.                     }
  105.                 }
  106.                 tr.Commit();
  107.             }
  108.  
  109.             //Transaction acTransaction = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction();
  110.             //using (acTransaction)
  111.             //{
  112.             //    BlockTable blockTable = (BlockTable)acTransaction.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead);
  113.             //    SymbolTableEnumerator bTIterator = blockTable.GetEnumerator;
  114.             //    if (blockTable.Has(blockName))
  115.             //    {
  116.             //        bool moreRecords = false;
  117.             //        BlockTableRecord btr = default(BlockTableRecord);
  118.             //        ObjectId btrID = default(ObjectId);
  119.             //        bTIterator.Reset();
  120.             //        moreRecords = bTIterator.MoveNext;
  121.             //        bool success = false;
  122.             //        while (moreRecords)
  123.             //        {
  124.             //            btrID = bTIterator.Current;
  125.             //            btr = (BlockTableRecord)acTransaction.GetObject(btrID, OpenMode.ForRead);
  126.             //            if (btr.Name == blockName)
  127.             //            {
  128.             //                blockId = btr.ObjectId;
  129.             //                success = true;
  130.             //                break; // TODO: might not be correct. Was : Exit Do
  131.             //            }
  132.             //            moreRecords = bTIterator.MoveNext;
  133.             //        }
  134.             //        if (!success)
  135.             //        {
  136.             //            throw new System.Exception("Look up failure for block: " + blockName);
  137.             //        }
  138.             //    }
  139.             //    else
  140.             //    {
  141.             //        throw new System.Exception("Block" + blockName + " does not exist in the current document...");
  142.             //    }
  143.             //    acTransaction.Commit();
  144.             //}
  145.             return blockId;
  146.         }
  147.  
  148.         public void CreateReference(Point3d Location, BlockTableRecord blockTableRec)
  149.         {
  150.             if (_acBlockReference == null)
  151.             {
  152.                 Transaction transaction = TestClass.GetAutocadTransaction();
  153.                 using (transaction)
  154.                 {
  155.                     _acBlockReference = new BlockReference(Location, _parentBlockID);
  156.                     if (_acBlockReference != null && _acBlockReference.IsDynamicBlock)
  157.                     {
  158.                         ObjectId id = blockTableRec.AppendEntity(_acBlockReference);
  159.                         if (_acBlockReference.IsNewObject)
  160.                         {
  161.                             transaction.AddNewlyCreatedDBObject(_acBlockReference, true);
  162.                         }
  163.                         //transaction.Commit();
  164.                         _acBlockReference.ScaleFactors = new Scale3d(_scaleX, _scaleY, _scaleZ);
  165.                         //if (_layer != "<use current>")
  166.                         //{
  167.                         //    _acBlockReference.Layer = _layer;
  168.                         //}
  169.                         _acBlockReference.Rotation = _rotation;
  170.                     }
  171.                     transaction.Commit();
  172.                 }
  173.                 //System.Threading.Thread.Sleep(50);
  174.                 //transaction.Dispose();
  175.             }
  176.             else
  177.             {
  178.                 throw new System.Exception("Reference already created...");
  179.             }
  180.         }
  181.  
  182.         protected void SetProperty(string Name, double Value)
  183.                 {
  184.                         if (_acBlockReference != null) {
  185.                                 //System.Threading.Thread.Sleep(0);
  186.                                 bool success = false;
  187.                                 DynamicBlockReferencePropertyCollection propColl = _acBlockReference.DynamicBlockReferencePropertyCollection;
  188.                                 //DynamicBlockReferenceProperty prop = default(DynamicBlockReferenceProperty);
  189.                 foreach (DynamicBlockReferenceProperty prop in propColl)
  190.                 {
  191.                                         if (!prop.ReadOnly && prop.PropertyName.ToUpper() == Name.ToUpper()) {
  192.                                                 prop.Value = Value;
  193.                                                 //AcadEditor.WriteLine("Successfully set property " + Name + " on block reference: " + _acBlockReference.Name);
  194.                                                 success = true;
  195.                                                 break; // TODO: might not be correct. Was : Exit For
  196.                                         }
  197.                                 }
  198.                                 if (!success) {
  199.                                         //AcadEditor.WriteLine("Failed to set property " + Name + " on block reference: " + _acBlockReference.Name);
  200.                                         throw new System.Exception(_parentBlockName + " does not have a property " + Name);
  201.                                 }
  202.                         } else {
  203.                                 throw new System.Exception(_parentBlockName + " is not a dynamic block.");
  204.                         }
  205.                 }
  206.     }
  207.  
  208.     public class AcadEditor
  209.     {
  210.  
  211.         public static void ExecuteCommand(string command)
  212.         {
  213.             //Application.DocumentManager.MdiActiveDocument.SendStringToExecute(Strings.Chr(27) + Strings.Chr(27) + command, true, false, true);
  214.         }
  215.  
  216.         public static void WriteLine(string message)
  217.         {
  218.             //Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(Constants.vbCrLf + message + Constants.vbCrLf);
  219.         }
  220.  
  221.         public static void ReportError(System.Exception ex)
  222.         {
  223.             //string message = ex.Message + Constants.vbCrLf + Constants.vbCrLf + ex.StackTrace;
  224.             //message += Constants.vbCrLf + Constants.vbCrLf + ex.Source;
  225.             //if (ex.InnerException != null)
  226.             //{
  227.             //    message += Constants.vbCrLf + Constants.vbCrLf + ex.InnerException.Message;
  228.             //}
  229.             //Interaction.MsgBox(message, MsgBoxStyle.Critical, "Error");
  230.         }
  231.  
  232.     }
  233.