Code Red > .NET

Does the .NET API have an equivalent to ActiveX AcadEntity class?

<< < (2/3) > >>

gile:
You cannot use a DBObject instance outside of the transaction used to open it or which it has been added to.
So, you can use the ObjectId of the DBObject from one transaction to another one but typically you should start a transaction in the main method and pass it as argument to the called method (or get the top transaction from these methods).

Anyway, most of the time, you can avoid adding a temporary Entity to the database.
For geometical purposes you should use the non database resident objects from the Geometry namespace (LineSegment2d/3d, CircularArc2d/3d, ...).

For entities which are only use to create other entities, you can simply Dispose() of the entity instead of adding it and erase it.
Here's an example which creates a cylinder by extruding a circle. it needs to create a Circle and a Region with this circle to extrude Region. Neither the Circle nor the Region are added to the Database, both are disposed after the region is created (with a using statement).

--- Code - C#: ---        static ObjectId CreateCylinder(Database db, Point3d center, Vector3d normal, double radius, double height)        {            ObjectId id;            using (var tr = db.TransactionManager.StartTransaction())            {                using (var circle = new Circle(center, normal, radius))                {                    var curves = new DBObjectCollection();                    curves.Add(circle);                    var regions = Region.CreateFromCurves(curves);                    using (var region = (Region)regions[0])                    {                        var cylinder = new Solid3d();                        cylinder.Extrude(region, height, 0.0);                        var space = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);                        id = space.AppendEntity(cylinder);                        tr.AddNewlyCreatedDBObject(cylinder, true);                    } // <- disposing the region                } // <- disposing the circle                tr.Commit();            }            return id;        }

pjm8765:
Thanks Gile.  Really?  I can't say I'm surprised...I've seen more abysmal programming/design whilst working with design tools (Tekla as well as AutoCAD) in the last two years than in my entire previous career put together.  Not yours I might add, but from the programmers I have inherited work from (anyone for a 10K+ line procedure?).  Now I've got a new build project I wanted to not have to compromise, have a decent OOP design and to not have to go anywhere near COM.  This kind of puts a dent in that. 

But I can work with these ObjectId objects.  I'm not working with one big fat transaction if I can possibly avoid it...above all else I want readability (encapsulation) and want to avoid passing parameters around like sweeties at a kids party. 

In this case I need a visible set of objects, used during the command, mainly for selection purposes.  But, I get your point about being able to use these classes to get the job done without mucking about with the database.

I shall plough on until I hit the next brick wall.

pjm8765:
And it gets worse.  This ObjectId class is non nullable, so I can't set a default value to return.  So I now have to lose my error handler in my business class or the code won't compile.

I'm beginning to wonder if I'd be better off using the ActiveX API after all (with it's use of the detestable Object class and even worse, arrays).  It doesn't seem to matter which way I turn, working with Autodesk products involves some sort of fudge.

Atook:
For ObjectID you can return ObjectID.Null

I've got an Erase function that looks like this:

--- Code - C#: ---/// <summary>/// Erase the object  from the current document/// </summary>/// <param name="id">ObjectID of the object to be erased</param>public static bool Erase(ObjectId id){  if (id == ObjectId.Null) return false;  using (Active.Document.LockDocument())  {    using (Transaction acTr = Active.Document.TransactionManager.StartTransaction())    {      DBObject obj = acTr.GetObject(id, OpenMode.ForWrite, true);      if (obj != null)      {        obj.Erase(true);        acTr.Commit();        return true;      }      acTr.Commit();      return false;    }   }}You could return the objectID from your database elements whenever you create one, maybe something like:

--- Code - C#: ---public ObjectID MakeCircle (pt center, double radius){  ObjectID result = ObjectID.Null;  ...  result = curSpace.AppendEntity(br);  ...  return result;}
Also, to reduce transactions, you could pass the Erase function an ObjectIDCollection, and use one transaction to erase the whole collection (via iteration)

Now you've got something like this:

--- Code - C#: ---ObjectIdCollection tempObjects = new ObjectIdCollection();...// Do your stuff, make your temp objectstempObjects.Add(MakeCircle(centerPt, 4));...// Do more stuff//Erase them all at onceEraseObjects(tempObjects);
As Gile said, better not to actually add to the database (maybe you can show your temp objects with a jig?), but this gets you started.

gile:

--- Quote from: pjm8765 on May 03, 2019, 11:07:38 AM ---And it gets worse.  This ObjectId class is non nullable, so I can't set a default value to return.

--- End quote ---
ObjectId has a Null static property and a IsNull instance property.


--- Quote from: pjm8765 on May 03, 2019, 10:28:06 AM ---But I can work with these ObjectId objects.  I'm not working with one big fat transaction if I can possibly avoid it...above all else I want readability (encapsulation) and want to avoid passing parameters around like sweeties at a kids party. 

--- End quote ---
Using the top transaction allows you to work with DBObjects instead of ObjectId and passing the transaction to the called method is not mandatory.
By my side, I like to use Extension methods called from within the top transaction, here's a little example to replace your 'AddCircleToDrawing' method.


--- Code - C#: ---    static class Extension    {        public static T GetObject<T>(            this ObjectId id,            OpenMode mode = OpenMode.ForRead,            bool openErased = false,            bool forceOpenOnLockedLayer = false) where T : DBObject        {            if (id.IsNull) throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NullObjectId);            Transaction tr = id.Database.TransactionManager.TopTransaction;            if (tr == null) throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);            return (T)tr.GetObject(id, mode, openErased, forceOpenOnLockedLayer);        }         public static Circle AddCircle(this BlockTableRecord owner, Point3d center, double radius, string layerName)        {            if (owner == null) throw new ArgumentNullException("owner");            var layerTable = owner.Database.LayerTableId.GetObject<LayerTable>();            if (!layerTable.Has(layerName)) throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.BadLayerName);            var tr = owner.Database.TransactionManager.TopTransaction;            var circle = new Circle() { Center = center, Radius = radius, Layer = layerName };            owner.AppendEntity(circle);            tr.AddNewlyCreatedDBObject(circle, true);            return circle;        }    }
In the main method

--- Code - C#: ---            using (var tr = db.TransactionManager.StartTransaction())            {                var space = db.CurrentSpaceId.GetObject<BlockTableRecord>(OpenMode.ForWrite);                var easiBaseOutline = space.AddCircle(insertPoint, 600, LayerNames.TemporaryEntity);                // do your stuff with 'easiBaseOutline'                tr.Commit();            }
Search this forum for GetObjects, you'll find more dicussions about such extension methods.

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version