TheSwamp

Code Red => .NET => Topic started by: pjm8765 on June 26, 2019, 06:49:35 AM

Title: Unable to add attribute definitions to a block after it's been created
Post by: pjm8765 on June 26, 2019, 06:49:35 AM
I get an error "one or more objects in this drawing cannot be saved to the specified format" when trying to save after having run the code below.

Code: [Select]
        private void EnsureJobHasAllAttrbuteDefinitions()
        {
            Document thisDrawing = null;
            BlockTable blockTable;
            ObjectId jobDetailsBlockObjectId;

            try
            {
                thisDrawing = AutoCADUtility.GetDocument();

                using (Transaction transaction = thisDrawing.Database.TransactionManager.StartTransaction())
                {
                    blockTable = transaction.GetObject(thisDrawing.Database.BlockTableId, OpenMode.ForRead) as BlockTable;

                    if (blockTable.Has(BlockNames.JobDetails))
                    {
                        jobDetailsBlockObjectId = blockTable[BlockNames.JobDetails];

                        AddMissingAttribute(jobDetailsBlockObjectId, JobDetailsBlockAttribute.Contractor);
                        AddMissingAttribute(jobDetailsBlockObjectId, JobDetailsBlockAttribute.Merchant);
                        AddMissingAttribute(jobDetailsBlockObjectId, JobDetailsBlockAttribute.ProjectName);
                        AddMissingAttribute(jobDetailsBlockObjectId, JobDetailsBlockAttribute.DrawingName);
                        AddMissingAttribute(jobDetailsBlockObjectId, JobDetailsBlockAttribute.WideWalls);
                        AddMissingAttribute(jobDetailsBlockObjectId, JobDetailsBlockAttribute.Depot);
                    }

                    transaction.Commit();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Job.EnsureJobHasAllAttrbuteDefinitions : " + ex.Message, MessageHeadings.ExceptionError);
            }
        }
        private void AddMissingAttribute(ObjectId jobDetailsBlockObjectId, string attributeName)
        {
            Document thisDrawing;
            Transaction transaction;
            AttributeDefinition attribute;
            BlockTableRecord jobDetailsBlock;
            bool attributeExists = false;

            try
            {
                thisDrawing = AutoCADUtility.GetDocument();

                transaction = thisDrawing.Database.TransactionManager.TopTransaction;

                jobDetailsBlock = transaction.GetObject(jobDetailsBlockObjectId, OpenMode.ForRead) as BlockTableRecord;

                if (jobDetailsBlock.HasAttributeDefinitions)
                {
                    foreach (ObjectId entity in jobDetailsBlock)
                    {
                        DBObject entityObject = transaction.GetObject(entity, OpenMode.ForRead) as DBObject;

                        if (entityObject is AttributeDefinition)
                        {
                            attribute = entityObject as AttributeDefinition;

                            if (attribute.Tag == attributeName)
                            {
                                attributeExists = true;
                            }
                        }
                    }
                }

                if (attributeExists == false)
                {
                    //If the attribute has not been found, add it
                    attribute = new AttributeDefinition(new Point3d(0, 510, 0), "", attributeName, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = false;
                    attribute.Layer = "0";
                    attribute.ColorIndex = 256;               //Color by layer

                    jobDetailsBlock.UpgradeOpen();

                    jobDetailsBlock.AppendEntity(attribute);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Job.AddMissingAttribute : " + ex.Message, MessageHeadings.ExceptionError);
            }
        }


I'm doing this because I have extra attributes I have added to a block and I need to make sure older drawings get this block updated with these attributes.

Title: Re: Unable to add attribute definitions to a block after it's been created
Post by: n.yuan on June 26, 2019, 01:39:50 PM
It seems to me that in the "AddMissingAttribute()" method, you missed

transaction.AddNewlyCreatedDBObject(attribute, true);

after the new attribute definition being appended to "jobDetailBlock" object. That is why the said error shows up when saving drawing: the newly added attribute definitions were not included in Transaction, thus not save-able.
Title: Re: Unable to add attribute definitions to a block after it's been created
Post by: pjm8765 on June 27, 2019, 04:35:30 AM
Thanks very, that works a treat. 

So, when the attributes are created and added to the new Block table record there is some sort of recursive feature that adds all of the new artefacts to the transaction, but when we're adding them individually to an existing block table record this doesn't happen.  Nice bit of inconsistent design, but I suppose it's par for the course. 

Mind you, I've just got curious about that 'Add' flag in the AddNewlyCreatedDBObject method and found this https://www.theswamp.org/index.php?topic=42055.0 (https://www.theswamp.org/index.php?topic=42055.0) topic....that was worth a laugh.  :thinking:
Title: Re: Unable to add attribute definitions to a block after it's been created
Post by: MickD on June 27, 2019, 05:02:29 AM
No, transactions and how they are used is pretty consistent, what happened is you are creating another inner transaction with your AddMissingAttribute method but not adding the new attribute objects _with_ that inner transaction or committing the inner transaction therefore they don't 'stick'.
hth
Title: Re: Unable to add attribute definitions to a block after it's been created
Post by: pjm8765 on June 27, 2019, 07:13:55 AM
Its not so much the use of the transaction.AddNewlyCreatedObject method in the AddMissingAttribute routine, above, as its use when the attribute definitions are created in the first place:

Code: [Select]
        private ObjectId CreateEasiBaseBlock(EasiBaseProperties easiBaseProperties)
        {
            ObjectId blockId = ObjectId.Null;
            Document thisDrawing;
            BlockTable blockTable;
            Transaction transaction;
            Circle innerCircle;
            Circle outerCircle;
            AttributeDefinition attribute;

            try
            {
                thisDrawing = AutoCADUtility.GetDocument();

                transaction = thisDrawing.Database.TransactionManager.TopTransaction;

                blockTable = transaction.GetObject(thisDrawing.Database.BlockTableId, OpenMode.ForRead) as BlockTable;

                using (BlockTableRecord blockTableRecord = new BlockTableRecord())
                {
                    blockTableRecord.Name = BlockNames.EasiBase;

                    // Insert the block near 0,0,0 but not on it
                    blockTableRecord.Origin = new Point3d(0, 1000, 0);

                    // Add inner circle to the block
                    innerCircle = new Circle();

                    innerCircle.Center = new Point3d(0, 1000, 0);
                    innerCircle.Radius = 600;
                    innerCircle.Layer = LayerNames.EasiBase;
                    innerCircle.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(innerCircle);

                    // Add inner circle to the block
                    outerCircle = new Circle();

                    outerCircle.Center = new Point3d(0, 1000, 0);
                    outerCircle.Radius = 650;
                    outerCircle.Layer = LayerNames.EasiBase;
                    outerCircle.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(outerCircle);

                    //Add the attributes
                    attribute = new AttributeDefinition(new Point3d(0, 1800, 0), "", EasiBaseBlockAttribute.SiteRef, EasiBaseBlockAttribute.SiteRef, ObjectId.Null);
                    attribute.Height = 80;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 1000, 0), "", EasiBaseBlockAttribute.EasiBaseSizeId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 999, 0), "", EasiBaseBlockAttribute.CoverLevel, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 998, 0), "", EasiBaseBlockAttribute.InvertLevel, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 997, 0), "", EasiBaseBlockAttribute.OverallPREDLReference, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 996, 0), "", EasiBaseBlockAttribute.MouldTypeId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 995, 0), "", EasiBaseBlockAttribute.SteelRingDepthId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 994, 0), "", EasiBaseBlockAttribute.NumberOfSeatingRings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 993, 0), "", EasiBaseBlockAttribute.CoverSlabDepthId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 992, 0), "", EasiBaseBlockAttribute.NumberOf250Rings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 991, 0), "", EasiBaseBlockAttribute.NumberOf500Rings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 990, 0), "", EasiBaseBlockAttribute.NumberOf750Rings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 989, 0), "", EasiBaseBlockAttribute.NumberOf1000Rings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 990, 0), "", EasiBaseBlockAttribute.EasiBaseId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    //Add the block to the record
                    blockTable.UpgradeOpen();

                    blockTable.Add(blockTableRecord);

                    transaction.AddNewlyCreatedDBObject(blockTableRecord, true);

                    blockId = blockTableRecord.Id;     
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("EasiBase.CreateEasiBaseBlock : " + ex.Message, MessageHeadings.ExceptionError);
            }

            return blockId;
        }

I would expect to see each artefact in the block to be added to the transaction separately.  Its not a problem.  I just like to understand the reasoning for it...well, within reason (it's not like I'm doing this for fun).

Title: Re: Unable to add attribute definitions to a block after it's been created
Post by: MickD on June 27, 2019, 07:35:41 AM
Yes, I see what you mean.

I'm guessing that when a block is created/added to the db it looks for any associated attribute records then adds each one to the attributes table one at a time when the block is added via the current transaction. When you want to add them later you need to add each record one at a time with the current transaction.

Title: Re: Unable to add attribute definitions to a block after it's been created
Post by: pjm8765 on June 27, 2019, 09:06:10 AM
I suspect that the database structure is such that in theory any object can be a child of any other object and the structure of blocks, block references and other objects is an artificial construct laid over that (a bit like the keywords of a language being processed by a parser)....and so this method recursively processes each child (and child of child etc). 

Anyway I've done my learning for today, I'm going to switch my brain off again and carry on with my work.
Title: Re: Unable to add attribute definitions to a block after it's been created
Post by: n.yuan on June 27, 2019, 10:03:31 AM
Its not so much the use of the transaction.AddNewlyCreatedObject method in the AddMissingAttribute routine, above, as its use when the attribute definitions are created in the first place:

Code: [Select]
        private ObjectId CreateEasiBaseBlock(EasiBaseProperties easiBaseProperties)
        {
            ObjectId blockId = ObjectId.Null;
            Document thisDrawing;
            BlockTable blockTable;
            Transaction transaction;
            Circle innerCircle;
            Circle outerCircle;
            AttributeDefinition attribute;

            try
            {
                thisDrawing = AutoCADUtility.GetDocument();

                transaction = thisDrawing.Database.TransactionManager.TopTransaction;

                blockTable = transaction.GetObject(thisDrawing.Database.BlockTableId, OpenMode.ForRead) as BlockTable;

                using (BlockTableRecord blockTableRecord = new BlockTableRecord())
                {
                    blockTableRecord.Name = BlockNames.EasiBase;

                    // Insert the block near 0,0,0 but not on it
                    blockTableRecord.Origin = new Point3d(0, 1000, 0);

                    // Add inner circle to the block
                    innerCircle = new Circle();

                    innerCircle.Center = new Point3d(0, 1000, 0);
                    innerCircle.Radius = 600;
                    innerCircle.Layer = LayerNames.EasiBase;
                    innerCircle.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(innerCircle);

                    // Add inner circle to the block
                    outerCircle = new Circle();

                    outerCircle.Center = new Point3d(0, 1000, 0);
                    outerCircle.Radius = 650;
                    outerCircle.Layer = LayerNames.EasiBase;
                    outerCircle.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(outerCircle);

                    //Add the attributes
                    attribute = new AttributeDefinition(new Point3d(0, 1800, 0), "", EasiBaseBlockAttribute.SiteRef, EasiBaseBlockAttribute.SiteRef, ObjectId.Null);
                    attribute.Height = 80;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 1000, 0), "", EasiBaseBlockAttribute.EasiBaseSizeId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 999, 0), "", EasiBaseBlockAttribute.CoverLevel, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 998, 0), "", EasiBaseBlockAttribute.InvertLevel, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 997, 0), "", EasiBaseBlockAttribute.OverallPREDLReference, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 996, 0), "", EasiBaseBlockAttribute.MouldTypeId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 995, 0), "", EasiBaseBlockAttribute.SteelRingDepthId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 994, 0), "", EasiBaseBlockAttribute.NumberOfSeatingRings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 993, 0), "", EasiBaseBlockAttribute.CoverSlabDepthId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 992, 0), "", EasiBaseBlockAttribute.NumberOf250Rings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 991, 0), "", EasiBaseBlockAttribute.NumberOf500Rings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 990, 0), "", EasiBaseBlockAttribute.NumberOf750Rings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 989, 0), "", EasiBaseBlockAttribute.NumberOf1000Rings, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    attribute = new AttributeDefinition(new Point3d(100, 990, 0), "", EasiBaseBlockAttribute.EasiBaseId, "", ObjectId.Null);
                    attribute.Height = 0.5;
                    attribute.Visible = true;
                    attribute.Layer = LayerNames.EasiBase;
                    attribute.ColorIndex = 256;               //Color by layer

                    blockTableRecord.AppendEntity(attribute);

                    //Add the block to the record
                    blockTable.UpgradeOpen();

                    blockTable.Add(blockTableRecord);

                    transaction.AddNewlyCreatedDBObject(blockTableRecord, true);

                    blockId = blockTableRecord.Id;     
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show("EasiBase.CreateEasiBaseBlock : " + ex.Message, MessageHeadings.ExceptionError);
            }

            return blockId;
        }

I would expect to see each artefact in the block to be added to the transaction separately.  Its not a problem.  I just like to understand the reasoning for it...well, within reason (it's not like I'm doing this for fun).

Well, when you create a new compound DBObject, such as BlockTableRecord, or a BlockReference with AttributeReferences, there are 2 ways to do it in regards of adding it to transaction:

1. you can create a new instance of it, and then add sub-objects to them. Then you add/append the compound object to a proper owner of the database, and you only need to add this compound object to the transaction, not each of sub-objects in it.

2. you can create the new compound object, add/append it to proper owner in DB, then add it to transaction (at this point, the new compound object has become DB-residing object, but you can still retrack by aborting the transaction, of course). Now, you can add sub-objects into the compound object, as you can see, for the newly added sub-objects, they need to be added to transaction.

When creating new compound objects, using approach 1. would be the most natural, logical, but I do see code uses approach 2 from time to time (including myself). In your case, since you are adding sub-object (AttributeDefinition) to existing, DB-residing compound object (BlockTableRecord, which means when the compound object is opened in transaction, itself and all sub-objects in it are in the transaction), the new object you are trying to add has to be added to the transaction (i.e. approach 2).

We may think (as you have described), why Autodesk not make the new sub-object automatically being added to the transaction when added/appended to the compound object now that it is already in a transaction? Well, consider the method name: AddNewlyCreatedDBObject(), which not only adds objects to transaction, but also flag them as "newly added", so that if yo want to roll back, only newly added objects are removed from the DB-residing compound object.