Author Topic: Can You Break This ?  (Read 4134 times)

0 Members and 1 Guest are viewing this topic.

TheMaster

  • Guest
Can You Break This ?
« on: February 14, 2013, 06:06:57 PM »
I'm posting this to show that in spite of what we see coming from various sources of information on building sound, robust AutoCAD solutions, the myth that old dogs can't learn new tricks is pure rubbish.

The attached assembly and source file show an example of how overrules can be leveraged to assign distinct values to objects or data contained in objects, in a robust and deterministic way (the same way AutoCAD assigns handles to newly-appended objects), that eliminates the potential for errors that could result from leaving invalid data laying around until someone decides it's time to clean it up.

If you can find a way to defeat the design by using the AutoCAD UI to create new insertions of the block in which the value in the attribute is not distinct (new insertions only, not by editing the attributes of an existing insertion), I would definitely like to know how you did it.

Of course, this code probably doesn't work, probably has a few problems that I don't know about, but what the heck, I'm gonna post it anway. 8-)

See the comments in the attached file. 
« Last Edit: February 14, 2013, 07:06:33 PM by TT »

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Can You Break This ?
« Reply #1 on: February 14, 2013, 06:16:49 PM »
I'll give it a go tomorrow, I've got a few routines that do mass insertions  :-D

Jeff H

  • Needs a day job
  • Posts: 6151
Re: Can You Break This ?
« Reply #2 on: February 14, 2013, 07:00:11 PM »
Tony I have not tested this other than if it would load and recognize command in 2013,
Let me know if you want me to delete this post, but was just uploading a version built for 2013 for anyone else who wanted it.

I made no changes to your source code and just used your AutoNumberAttributeOverrule.cs file in a project targeted framework 4.0 and included AcCoreMgd reference.

Here were the steps took to create AutoNumberAttributeOverrule2013.dll that is attached.
--Created a new solution using Class Library template called AutoNumberAttributeOverrule2013
--Added your AutoNumberAttributeOverrule.cs file
--Added reference to
   --AcCoreMgd.dll
   --AcDbMgd.dll
   --AcMgd.dll
--Built Solution
--Copied AutoNumberAttributeOverrule2013.dll from build and your AutoNumberAttributeOverrule.cs into folder   
   AutoNumberAttributeOverrule13



TheMaster

  • Guest
Re: Can You Break This ?
« Reply #3 on: February 14, 2013, 07:10:32 PM »
Tony I have not tested this other than if it would load and recognize command in 2013,
Let me know if you want me to delete this post, but was just uploading a version built for 2013 for anyone else who wanted it.

I made no changes to your source code and just used your AutoNumberAttributeOverrule.cs file in a project targeted framework 4.0 and included AcCoreMgd reference.

Here were the steps took to create AutoNumberAttributeOverrule2013.dll that is attached.
--Created a new solution using Class Library template called AutoNumberAttributeOverrule2013
--Added your AutoNumberAttributeOverrule.cs file
--Added reference to
   --AcCoreMgd.dll
   --AcDbMgd.dll
   --AcMgd.dll
--Built Solution
--Copied AutoNumberAttributeOverrule2013.dll from build and your AutoNumberAttributeOverrule.cs into folder   
   AutoNumberAttributeOverrule13


Jeff - Thanks. Sorry, I forgot to mentioned that the assembly targeted AutoCAD 2012.

TheMaster

  • Guest
Re: Can You Break This ?
« Reply #4 on: February 14, 2013, 07:27:08 PM »
Just so you know, there's a minor bug in that code that will start assigning values at the incorrect value when existing insertions are found. It should be one greater than the highest existing value found when the command runs.

Gasty

  • Newt
  • Posts: 90
Re: Can You Break This ?
« Reply #5 on: February 14, 2013, 09:26:01 PM »
Hi Tony,

Very nice code, as for test,  with divide and measure commands (block option) got funny results, no attribs at all in the blocks.

Gaston Nunez


TheMaster

  • Guest
Re: Can You Break This ?
« Reply #6 on: February 15, 2013, 06:21:49 AM »
Hi Tony,

Very nice code, as for test,  with divide and measure commands (block option) got funny results, no attribs at all in the blocks.

Gaston Nunez

Hi Gaston, and thanks.

If I'm not mistaken DIVIDE/MEASURE has never supported blocks with attributes. The standard 'trick' is to insert the block with the attribute inside another block, use that with DIVIDE/MEASURE and then explode all of the resulting block insertions.

Gasty

  • Newt
  • Posts: 90
Re: Can You Break This ?
« Reply #7 on: February 15, 2013, 08:41:30 AM »
Hi Tony,

Really I was not aware of that issue with divide/measure, but it make a lot of sense.

Best regards,

Gaston Nunez

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Can You Break This ?
« Reply #8 on: February 15, 2013, 07:51:24 PM »
Tony,

I threw my hardest working block insertion routine at this and didn't find any issues.  I did however break it with ctrl+c copy and ctrl+v paste, the number of the jigged paste block was incremented, then upon insertion was incremented again. The fix was to modify your Close override to check that the object was transaction resident:

Code - C#: [Select]
  1.                 public override void Close( DBObject obj )
  2.                 {
  3.                         if( obj.Database == this.db && obj.IsNewObject && obj.IsWriteEnabled && !obj.IsTransactionResident )  //Transient objects from ctrl+v paste return true here and GetNextValue is not called
  4.                         {
  5.                                 AttributeReference attref = obj as AttributeReference;
  6.                                 if( attref != null && attref.Tag.ToUpper() == this.tag )
  7.                                 {
  8.                     attref.TextString = GetNextValue().ToString();
  9.                                 }
  10.                         }
  11.                         base.Close( obj );
  12.                 }



I think it would be great to have another command added to disable the overrule (so we could turn it onto a different attribute in the same drawing session, or even just a different drawing) and also thought it would useful to implement on a per document basis by making it non static with the following modifications:

Code - C#: [Select]
  1.         public class AutoNumCommands
  2.         {
  3.         [CommandMethod( "AUTONUM" )]
  4.                 public void AutoNumExample()
  5.                 {
  6.                         try
  7.                         {
  8.                                 Document doc = Application.DocumentManager.MdiActiveDocument;
  9.                
  10.                 if( overrule != null )
  11.                                 {
  12.                                         PromptIntegerOptions peo = new PromptIntegerOptions( "\nNew seed value: " );
  13.                                         peo.UseDefaultValue = true;
  14.                                         peo.DefaultValue = overrule.Next;
  15.                                         peo.AllowNegative =false;
  16.                                         peo.AllowZero = false;
  17.                                         while( true )
  18.                                         {
  19.                                                 PromptIntegerResult per = doc.Editor.GetInteger( peo );
  20.                                                 if( per.Status != PromptStatus.OK )
  21.                                                         return;
  22.                                                 if( per.Value < overrule.Next )
  23.                                                 {
  24.                                                         doc.Editor.WriteMessage( "\nValue cannot be less than {0},", overrule.Next );
  25.                                                         continue;
  26.                                                 }
  27.                                                 doc.Editor.WriteMessage( "\nNew seed value: {0}", overrule.Next );
  28.                                                 overrule.Next = per.Value;
  29.                                                 return;
  30.                                         }
  31.                                 }
  32.                                 var pneo = new PromptNestedEntityOptions( "\nSelect attribute: " );
  33.                                 var pner = doc.Editor.GetNestedEntity( pneo );
  34.                                 if( pner.Status != PromptStatus.OK )
  35.                                         return;
  36.                                 if( !pner.ObjectId.ObjectClass.IsDerivedFrom( RXClass.GetClass( typeof( AttributeReference ) ) ) )
  37.                                 {
  38.                                         doc.Editor.WriteMessage( "\nInvalid selection, must select a block attribute" );
  39.                                         return;
  40.                                 }
  41.                                 ObjectId blockId = ObjectId.Null;
  42.                                 string tag = string.Empty;
  43.                                 string name = string.Empty;
  44.                                 using( Transaction trans = doc.TransactionManager.StartTransaction() )
  45.                                 {
  46.                                         var attref = pner.ObjectId.GetObject<AttributeReference>();
  47.                                         tag = attref.Tag.ToUpper();
  48.                                         var blkref = attref.OwnerId.GetObject<BlockReference>();
  49.                                         blockId = blkref.DynamicBlockTableRecord;
  50.                                         name = blockId.GetObject<BlockTableRecord>().Name;
  51.                                         trans.Commit();
  52.                                 }
  53.                                 overrule = new AutoNumberAttributeOverrule( blockId, tag );
  54.                                 overrule.Enabled = true;
  55.                                 doc.Editor.WriteMessage( "\nAutomatically incrementing attribute {0} in {1}", tag, name );
  56.                         }
  57.                         catch( System.Exception ex )
  58.                         {
  59.                                 Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( ex.ToString() );
  60.                                 throw ex;
  61.                         }
  62.                 }
  63.  
  64.         [CommandMethod("StopAutoNum")]
  65.         public void StopAutoNumExample()
  66.         {
  67.             if (overrule!=null)
  68.             {
  69.                 overrule.Dispose();
  70.                 overrule = null;
  71.             }
  72.         }
  73.        
  74.         public AutoNumCommands()
  75.         {
  76.             Application.DocumentManager.MdiActiveDocument.CloseWillStart += closeWillStart;
  77.  
  78.             //Application.QuitWillStart += quitWillStart;
  79.         }
  80.         void closeWillStart(object sender, EventArgs e)
  81.         {
  82.             Application.DocumentManager.MdiActiveDocument.CloseWillStart -= closeWillStart;
  83.             if (overrule != null)
  84.             {
  85.                 overrule.Dispose();
  86.                 overrule = null;
  87.             }
  88.         }
  89.  
  90.  
  91.         //void quitWillStart( object sender, EventArgs e )
  92.         //{
  93.         //    Application.QuitWillStart -= quitWillStart;
  94.         //    if( overrule != null )
  95.         //    {
  96.         //        overrule.Dispose();
  97.         //        overrule = null;
  98.         //    }
  99.         //}
  100.  
  101.         AutoNumberAttributeOverrule overrule = null;
  102.         //static AutoNumberAttributeOverrule overrule = null;
  103.         }


Also with respect to the seed value I made this mod:

Code - C#: [Select]
  1.                 static int GetSeedValue( ObjectId blockId, string tag )
  2.                 {
  3.                         int result = 0;
  4.                         using( Transaction tr = blockId.Database.TransactionManager.StartTransaction() )
  5.                         {
  6.                 result = blockId.GetObject<BlockTableRecord>()
  7.                     .GetAttributes(tag).Select(GetValue).Max() + 1;
  8.                                 tr.Commit();
  9.                         }
  10.                         return result;
  11.                 }

Comments are appreciated!

Cheers

Jeff H

  • Needs a day job
  • Posts: 6151
Re: Can You Break This ?
« Reply #9 on: February 15, 2013, 08:49:10 PM »
Does how the numbers get incremented really a bug or an issue?
Just trying to make sure I understand it.


I thought this was about how to solve the problem of being able to attach, map, display, or whatever but dealing with it being unique for each object.



TheMaster

  • Guest
Re: Can You Break This ?
« Reply #10 on: February 18, 2013, 05:45:10 PM »
Tony,

I threw my hardest working block insertion routine at this and didn't find any issues.  I did however break it with ctrl+c copy and ctrl+v paste, the number of the jigged paste block was incremented, then upon insertion was incremented again. The fix was to modify your Close override to check that the object was transaction resident:


Will, Thanks.

The problem you noted with pasteclip is actually not 'breaking' the code. To break the code or the design, you would have to find a way to create multiple new instances of the block that have the same value in the managed attribute (without changing attribute values after the fact). The problem with pasteclip doesn't do that, so you didn't break it :laugh:

Perhaps my use of the term 'AutoNumber' is a bit misleading, but that code is by no means a complete solution for automatic numbering, or even remotely near that . The code is an absolute bare-bones, minimal implementation of what was needed to demonstrate the use of an Overrule to automate assignment of distinct values. The only reason it works with attributes is because that makes the behavior of the Overrule easily observed and testable.

The change you made to the Close() method will not work, because creating new objects and adding them to a database does not require the use of a Transaction. And, I would guess there are some AutoCAD commands that may not even use Transactions, so IsTransactionResident can't be used to solve the problem with PASTECLIP.

Quote


I think it would be great to have another command added to disable the overrule (so we could turn it onto a different attribute in the same drawing session, or even just a different drawing)


As far as adding commands and so forth, like I mentioned, the code was only written to serve as a vehicle for demonstrating how Overrules might be used to automate assignment of values to newly-created objects, and I didn't really intend for it to be a user-level solution.

Quote
and also thought it would useful to implement on a per document basis by making it non static with the following modifications:

I'm afraid it's not that simple. Overrules operate at the object-level, so, what happens when you create multiple overrules, is that each of their methods will be called and passed the same object. Three overrules will result in three calls to each of their virtual methods for every DBObject, regardless of what Database the DBObject is from (including no database).

To implement database-specific behaviour without it becoming a major performance bottleneck, you can only use one overrule for all objects in all databases. You can use a Dictionary to store database-specific data keyed to the database, and use that to have an overrule use different parameters or data depending on the database, but multiple, full-time overrules for each open document is definitely out of the question. The overrule I posted in another thread that's running only while the OFFSET command is in progress is database-specific, but each overrule is only enabled when the OFFSET command is running in the associated document, which makes multiple, document-specific overrules feasable. Otherwise, for a full-time overrule, you can't have more than one without it becoming a major performance problem.

Here is some code that shows just how often the methods of an ObjectOverrule are called when there's no constraint or filter on the overrule.

Code - C#: [Select]
  1.  
  2. /// DebugOverrule.cs  Copyright (c) 2010  Tony Tanzillo
  3.  
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Text;
  8. using Autodesk.AutoCAD.Runtime;
  9. using Autodesk.AutoCAD.ApplicationServices;
  10. using Autodesk.AutoCAD.EditorInput;
  11.  
  12. /// This sample was written to help demonstrate
  13. /// the potential overhead associated with the use
  14. /// of Overrules, namely the ObjectOverrule, whose
  15. /// methods have the highest call frequency of all
  16. /// of Overrule types.
  17. ///
  18. /// Compile and load this code, and then issue the
  19. /// DEBUGOVER command to create an ObjectOverrule
  20. /// that counts the number of calls to its Open()
  21. /// and Close() overrides. Note that every virtual
  22. /// method of an ObjectOverrule is called even if
  23. /// it is not overridden in a concrete derived type,
  24. /// and calls to all of those virtual methods aren't
  25. /// being counted by this sample. The sample only
  26. /// counts the number of calls that are made to its
  27. /// Open() and Close() overrides.
  28. ///
  29. /// The current number of calls that have been made
  30. /// to the Overrule's Open() and Close() methods are
  31. /// shown in the AutoCAD status bar, and updated on
  32. /// each Idle notification.
  33. ///
  34. /// The Trace option displays each call to Close()
  35. /// on the AutoCAD console (careful, as this will
  36. /// flood the console if left on for any length of
  37. /// time). The Reset option resets the call count.
  38. ///
  39. /// After issuing DEBUGOVER, notice that little more
  40. /// than moving the mouse pointer results in calls to
  41. /// the Open() and Close() methods. Try doing routine
  42. /// AutoCAD editing and notice the number of calls to
  43. /// Open() and Close() as a result.
  44. ///
  45. /// Try using the AutoCAD MOVE command on a few dozen
  46. /// objects, and while dragging the selected objects,
  47. /// note the number of calls to Open() and Close() that
  48. /// result.
  49. ///
  50. /// What you observe should help to make it clear that
  51. /// while ObjectOverrule is a powerful abstraction that
  52. /// can be used to solve a broad range of problems, if
  53. /// not implemented correctly, it can adversely impact
  54. /// the performance of virtually everything a user does
  55. /// with AutoCAD.
  56.  
  57. namespace Autodesk.AutoCAD.DatabaseServices.MyExtensions
  58. {
  59.    public class DebugOverruleCommands
  60.    {
  61.       [CommandMethod( "DEBUGOVER" )]
  62.       public static void DebugOverruleCommand()
  63.       {
  64.          Document doc = Application.DocumentManager.MdiActiveDocument;
  65.          if( !DebugOverrule.IsAlive )
  66.          {
  67.             var instance = DebugOverrule.Instance;
  68.             doc.Editor.WriteMessage( "\nDebugOverrule enabled" );
  69.             return;
  70.          }
  71.          PromptKeywordOptions pko = new PromptKeywordOptions( "" );
  72.          pko.Keywords.Add( "Reset" );
  73.          pko.Keywords.Add( "Dispose" );
  74.          pko.Keywords.Add( "Trace" );
  75.          pko.Keywords.Add( "eXit" );
  76.          pko.Keywords.Default = "eXit";
  77.          pko.Message = string.Format( "\nCall count: {0}, ",
  78.             DebugOverrule.Instance.Count );
  79.          PromptResult result = doc.Editor.GetKeywords( pko );
  80.          if( result.Status != PromptStatus.OK )
  81.             return;
  82.          switch( result.StringResult )
  83.          {
  84.             case "Reset":
  85.                DebugOverrule.Instance.Reset();
  86.                break;
  87.             case "Dispose":
  88.                DebugOverrule.Instance.Dispose();
  89.                break;
  90.             case "Trace":
  91.                DebugOverrule.Instance.Trace ^= true;
  92.                break;
  93.             default:
  94.                break;
  95.          }
  96.       }
  97.  
  98.    }
  99.  
  100.    static class MyUtils
  101.    {
  102.       public static void WriteLine( string fmt, params object[] args )
  103.       {
  104.          Document doc = Application.DocumentManager.MdiActiveDocument;
  105.          if( doc != null )
  106.          {
  107.             if( !fmt.StartsWith( "\n" ) )
  108.                doc.Editor.WriteMessage( "\n" + fmt, args );
  109.             else
  110.                doc.Editor.WriteMessage( fmt, args );
  111.          }
  112.       }
  113.    }
  114.  
  115.    class DebugOverrule : ObjectOverrule
  116.    {
  117.       static RXClass rxclass = RXClass.GetClass( typeof( DBObject ) );
  118.       bool disposed = false;
  119.       Windows.Pane pane = null;
  120.       bool trace = false;
  121.       long count = 0;
  122.  
  123.       DebugOverrule()
  124.       {
  125.          Overrule.AddOverrule( rxclass, this, true );
  126.          Application.Idle += idle;
  127.          pane = new Windows.Pane();
  128.          pane.Style = Windows.PaneStyles.Normal;
  129.          Application.StatusBar.Panes.Insert( 1, pane );
  130.       }
  131.  
  132.       public override void Open( DBObject dbObject, OpenMode mode )
  133.       {
  134.          ++count;
  135.          base.Open( dbObject, mode );
  136.       }
  137.  
  138.       public override void Close( DBObject dbObject )
  139.       {
  140.          ++count;
  141.          if( trace )
  142.             MyUtils.WriteLine( "DebugOverrule.Close({0})\n",
  143.                dbObject.Handle.ToString() );
  144.          base.Close( dbObject );
  145.       }
  146.  
  147.       public bool Trace
  148.       {
  149.          get
  150.          {
  151.             return trace;
  152.          }
  153.          set
  154.          {
  155.             trace = value;
  156.          }
  157.       }
  158.  
  159.       void idle( object sender, EventArgs e )
  160.       {
  161.          this.pane.Text = string.Format( "DebugOverrule.Count = {0}", count );
  162.          Application.StatusBar.Update();
  163.       }
  164.  
  165.       public void Reset()
  166.       {
  167.          count = 0L;
  168.       }
  169.  
  170.       public long Count
  171.       {
  172.          get
  173.          {
  174.             return count;
  175.          }
  176.       }
  177.  
  178.       static DebugOverrule()
  179.       {
  180.          Application.QuitWillStart += quitWillStart;
  181.       }
  182.  
  183.       static void quitWillStart( object sender, EventArgs e )
  184.       {
  185.          if( instance != null )
  186.          {
  187.             instance.Dispose();
  188.             instance = null;
  189.          }
  190.       }
  191.  
  192.       static DebugOverrule instance = null;
  193.       internal static DebugOverrule Instance
  194.       {
  195.          get
  196.          {
  197.             if( instance == null )
  198.                instance = new DebugOverrule();
  199.             return instance;
  200.          }
  201.       }
  202.  
  203.       internal static bool IsAlive
  204.       {
  205.          get
  206.          {
  207.             return instance != null;
  208.          }
  209.       }
  210.  
  211.       protected override void Dispose( bool disposing )
  212.       {
  213.          if( disposing && !disposed )
  214.          {
  215.             Application.Idle -= idle;
  216.             Application.StatusBar.Panes.Remove( this.pane );
  217.             disposed = true;
  218.             Overrule.RemoveOverrule( rxclass, this );
  219.             if( instance == this )
  220.                instance = null;
  221.          }
  222.          base.Dispose( disposing );
  223.       }
  224.  
  225.    }
  226. }
  227.  
  228.  
« Last Edit: February 18, 2013, 07:38:56 PM by TT »

TheMaster

  • Guest
Re: Can You Break This ?
« Reply #11 on: February 19, 2013, 09:54:45 PM »
After looking at the issue WILL pointed out with PASTECLIP (thanks again WILL), I was able to figure out a solution to the problem which is quite simple (in case you're adapting this code to do automatic-numbering of attributes):

Inside the Close() override, Look at the value of the AttributeReference's BlockName property. If it starts with the string "A$C", it is most-likely a block that was brought in by PASTECLIP, and just exit without changing the TextString.

Code - C#: [Select]
  1.  
  2. public override void Close( DBObject obj )
  3. {
  4.    if( obj.Database == this.db && obj.IsNewObject && obj.IsWriteEnabled && obj.IsReallyClosing )  
  5.    {
  6.       AttributeReference attref = obj as AttributeReference;
  7.       if( attref != null && attref.Tag.ToUpper() == this.tag && ! attref.BlockName.StartsWith( "A$C" ) )
  8.       {
  9.               attref.TextString = GetNextValue().ToString();
  10.       }
  11.    }
  12.    base.Close( obj );
  13. }
  14.  
« Last Edit: February 20, 2013, 12:19:49 AM by TT »

Jeff H

  • Needs a day job
  • Posts: 6151
Re: Can You Break This ?
« Reply #12 on: February 20, 2013, 12:49:08 AM »
Speaking of overrides when it comes to something that is command specific is it normal to "override"  the command by means of UNDEFINE then loading your own command with same name, that does whatever functionality before and/or after invoking the original command.







TheMaster

  • Guest
Re: Can You Break This ?
« Reply #13 on: February 20, 2013, 12:53:01 AM »
Speaking of overrides when it comes to something that is command specific is it normal to "override"  the command by means of UNDEFINE then loading your own command with same name, that does whatever functionality before and/or after invoking the original command.

Not sure if you mean allow the replaced or built-in command to run normally and allow the user to directly interact with it, or if you mean to supply all input that it requires via acedCommand() or something. The latter will work, but the former will not because you can't pause perpetually until the built-in command ends, without something going wrong.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Can You Break This ?
« Reply #14 on: February 20, 2013, 10:59:28 AM »
Tony,

Thank you for that insightful explanation!  I see now how my apparent solution only suited the case of COPY vs PASTECLIP, and since it was only applying the overrule to non transaction resident objects it would not apply to any of my own routines which religiously use the transaction model.  This also reminds me of my recent discovery (Thank you Owen and Jeff!) that my vertical product wraps a transaction around my commands and the particular behavior

Noticing that when we perform PASTECLIP the object is transaction resident during the jig, and when we insert the object a new instance is created which is non transaction resident raises a couple questions for me:

-Is the transaction mechanism being used here as an easy way to undo the underlying BlockTableRecord cloning that must occur in the background for a new block definition to be inserted into the database in the event the user cancels the operation?

-Is the reason driving COPY command functioning outside of a transaction an artifact of older functionality, while PASTECLIP being a newer function is handled by newer methods?

I'm afraid it's not that simple. Overrules operate at the object-level, so, what happens when you create multiple overrules, is that each of their methods will be called and passed the same object. Three overrules will result in three calls to each of their virtual methods for every DBObject, regardless of what Database the DBObject is from (including no database).

To implement database-specific behaviour without it becoming a major performance bottleneck, you can only use one overrule for all objects in all databases. You can use a Dictionary to store database-specific data keyed to the database, and use that to have an overrule use different parameters or data depending on the database, but multiple, full-time overrules for each open document is definitely out of the question. The overrule I posted in another thread that's running only while the OFFSET command is in progress is database-specific, but each overrule is only enabled when the OFFSET command is running in the associated document, which makes multiple, document-specific overrules feasable. Otherwise, for a full-time overrule, you can't have more than one without it becoming a major performance problem.

When you mention using dictionaries to sort this out I'm picturing the dictionary call not being a part of the Close() override, but rather a document level event handler which would switch the affected DB and manage the respective properties for each document (seed value, database, tag, etc.) when the current document is changing, and changing those values handled by the Close() method to suit.  Is this how you would handle it?

After looking at the issue WILL pointed out with PASTECLIP (thanks again WILL), I was able to figure out a solution to the problem which is quite simple (in case you're adapting this code to do automatic-numbering of attributes):

Inside the Close() override, Look at the value of the AttributeReference's BlockName property. If it starts with the string "A$C", it is most-likely a block that was brought in by PASTECLIP, and just exit without changing the TextString.

I never would have anticipated this one small situation posing this significant of a problem! A$C is also the prefix of any dynamic block which has modified properties.  The only definitive way I can think of at this point to determine if this is a PASTECLIP operation would be to see if that was the running command.  There must be a simpler way...

Cheers

TheMaster

  • Guest
Re: Can You Break This ?
« Reply #15 on: February 20, 2013, 01:33:46 PM »
Tony,

Thank you for that insightful explanation!  I see now how my apparent solution only suited the case of COPY vs PASTECLIP, and since it was only applying the overrule to non transaction resident objects it would not apply to any of my own routines which religiously use the transaction model.  This also reminds me of my recent discovery (Thank you Owen and Jeff!) that my vertical product wraps a transaction around my commands and the particular behavior

Noticing that when we perform PASTECLIP the object is transaction resident during the jig, and when we insert the object a new instance is created which is non transaction resident raises a couple questions for me:

-Is the transaction mechanism being used here as an easy way to undo the underlying BlockTableRecord cloning that must occur in the background for a new block definition to be inserted into the database in the event the user cancels the operation?

Probably yes, since there's no other simple way to roll back the insertion of the external file.
Quote
-Is the reason driving COPY command functioning outside of a transaction an artifact of older functionality, while PASTECLIP being a newer function is handled by newer methods?

I suppose it could be, but I can't give you a definitive answer. There may also be an internal form of transaction that can't be seen from the API that could be in use. Or it could be as simple as using UNDO/Begin and UNDO/End like most LISP scripting does to achieve the same kind of transactional behavior.

Quote
When you mention using dictionaries to sort this out I'm picturing the dictionary call not being a part of the Close() override, but rather a document level event handler which would switch the affected DB and manage the respective properties for each document (seed value, database, tag, etc.) when the current document is changing, and changing those values handled by the Close() method to suit.  Is this how you would handle it?

No. Changes can be made to any open database, without it having to be active. So, an overrule needs to be prepared to deal with objects from any database, regardless of whether the database is the active database or not. The easiest way to do that is to use a Dictionary<Database, T> where T is the database-specific data, or some class that contains it, and just lookup the entry on each call. There's some overhead there, but not nearly as much as there is for multiple overrules, each servicing only one database.

Quote

I never would have anticipated this one small situation posing this significant of a problem! A$C is also the prefix of any dynamic block which has modified properties.  The only definitive way I can think of at this point to determine if this is a PASTECLIP operation would be to see if that was the running command.  There must be a simpler way...


A$C isn't the prefix of dynamic blocks with modified properties. Those are anonymous blocks matching the pattern "*Unnn". Although it may be the name that an XREF-BIND operation gives to blocks, that still shouldn't be a problem since the BlockName property of the AttributeReference is not the name of the block containing the attribute, it's the name of the block which the block containing the attribute is inserted into. IOW, it will almost always be the name of the model space or a paper space block.

I tried the code on both regular and dynamic blocks, and it seems to work fine here.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Can You Break This ?
« Reply #16 on: February 20, 2013, 08:47:50 PM »
Thank you Tony, I had gotten that mixed up with a drawing here I was using for reference.  I'm very glad at this point that I have not delved into any programs that make use of overrules yet as I still don't have a solid enough grasp on the beast to make effective use of them.  My work here is requiring that I make that leap soon and this has been an excellent lesson as I do so.

Cheers