best practice encapsulate acad code c#

best practice encapsulate acad code c#

Anonymous
Not applicable
1,265 Views
7 Replies
Message 1 of 8

best practice encapsulate acad code c#

Anonymous
Not applicable

I'd think by now there would be examples of best practice for encapsulating the verbose code needed to access acad objects, getting object Id's, opening closing transactions etc, so that code would be written only once then reused by .net application objects needing such interaction? can anyone point to posts like that?

0 Likes
1,266 Views
7 Replies
Replies (7)
Message 2 of 8

Anonymous
Not applicable

for example, I'm just beginning design phase of a c# app that will want to interact with acad, including writing objects and persisting references to objects via handles from one session to another. So my thought is to define an interface and implement things like getting the doc, getting the block tables, getting objects from their handles etc.  I would only want acad code in one place, not in every application object that will need to interact with acad to "draw itself" or "check if object was modified since last program run" etc.

thus in my AcadHelper(or whatever).cs I could have something like the following, but not sure if that kind of approach would work and how best to catch exceptions...

 

 public ObjectId ObjectIdFromHandle(string handle, Document doc)
        {
            Editor ed = doc.Editor;
            Database db = doc.Database;

            try
            {
                // Convert hexadecimal string to 64-bit integer
                long ln = Convert.ToInt64(handle, 16);
                // Now create a Handle from the long integer
                Handle hn = new Handle(ln);
                // And attempt to get an ObjectId for the Handle
                ObjectId id = db.GetObjectId(false, hn, 0);
                return id;
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage(
                  "Exception: " + ex
                );
            }
        }

0 Likes
Message 3 of 8

_gile
Consultant
Consultant

Hi,

 

By my side, I use 'Linq style' extension methods as the following examples, but I always explicitly use transactions.

Typically, I start a single transaction by main method (e.g. CommandMethod) with a using statement and use this transaction in the methods called within the using scope.

 

using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Autodesk.AutoCAD.DatabaseServices
{
    public static class DatabaseExtension
    {
        public static T GetObject<T>(
            this ObjectId id,
            OpenMode mode = OpenMode.ForRead,
            bool openErased = false,
            bool forceOpenOnLockedLayer = false) where T :DBObject
        {
            if (id == ObjectId.Null)
                throw new ArgumentNullException(nameof(id));

            var tr = id.Database.TransactionManager.TopTransaction;
            if (tr == null)
                throw new Runtime.Exception(ErrorStatus.NoActiveTransactions);

            return (T)tr.GetObject(id, mode, openErased, forceOpenOnLockedLayer);
        }

        public static IEnumerable<T> GetObjects<T>(
            this IEnumerable<ObjectId> source,
            OpenMode mode = OpenMode.ForRead,
            bool openErased = false,
            bool forceOpenOnLockedLayer = false) where T : DBObject
        {
            if (source == null)
                throw new ArgumentNullException("source");

            if (source.Any())
            {
                var tr = source.First().Database.TransactionManager.TopTransaction;
                if (tr == null)
                    throw new Runtime.Exception(ErrorStatus.NoActiveTransactions);

                RXClass outputClass = RXObject.GetClass(typeof(T));
                foreach (var id in source)
                {
                    if (id.ObjectClass == outputClass || id.ObjectClass.IsDerivedFrom(outputClass))
                    {
                        if (!id.IsErased || openErased)
                            yield return (T)tr.GetObject(id, mode, openErased, forceOpenOnLockedLayer);
                    }
                }
            }
        }

        public static IEnumerable<T> GetObjects<T>(
            this ObjectIdCollection source,
            OpenMode mode = OpenMode.ForRead,
            bool openErased = false,
            bool forceOpenOnLockedLayer = false) where T : DBObject
        {
            return source.Cast<ObjectId>().GetObjects<T>(mode, openErased, forceOpenOnLockedLayer);
        }

        public static IEnumerable<T> GetObjects<T>(
            this BlockTableRecord source,
            OpenMode mode = OpenMode.ForRead,
            bool openErased = false,
            bool forceOpenOnLockedLayer = false) where T : Entity
        {
            return source.Cast<ObjectId>().GetObjects<T>(mode, openErased, forceOpenOnLockedLayer);
        }

        public static IEnumerable<T> GetObjects<T>(
            this SymbolTable source,
            OpenMode mode = OpenMode.ForRead,
            bool openErased = false) where T : SymbolTableRecord
        {
            return source.Cast<ObjectId>().GetObjects<T>(mode, openErased);
        }
    }
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 4 of 8

JamesMaeding
Advisor
Advisor

I tend to agree things like this are the toughest to learn.

Keep in mind, the way a person teaching a beginner codes is attempting to reach entirely different goals than a production coder.

Also, stuff like a million carriage returns to keep code narrow to fit in some blog column is super annoying to me.

So "Best Practice" is really subjective.

 

The thing you describe "encapsulating code" is what I call making "helper libraries".

This link is a pretty good place to start:

https://www.theswamp.org/index.php?PHPSESSID=efshon7ij6ug7emjqm48c6ga94&topic=31866.0

 

Its not just how to slick out operations, its about how to separate your code into projects that can be used on many solutions.

So for me, I have a separate helper project when it requires some specific additional reference, or is just something I only need sometimes.

I have projects like this:

general, forms, database, ftp manipulation, vector geometry, computational geometry, GIS manipulation...and so on.

That way you build a solution to only contain the minimum needed.

good luck

 


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes
Message 5 of 8

_gile
Consultant
Consultant

The Database type already have a TryGetObjectId() method which requires a Handle instance instead of a string as argument.

You can overload this method using an extension method.

 

Snippet

public static bool TryGetObjectId(this Database db, string handle, out ObjectId id)
{
    long ln;
    if (!long.TryParse(handle, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out ln))
    {
        id = ObjectId.Null;
        return false;
    }
    return db.TryGetObjectId(new Handle(ln), out id);
} 

 

Using example:

 

Snippet

ObjectId id;
if (db.TryGetObjectId("FFF"out id))
{
    // do simething with id
}
else
{
    // "FFF" is not a valid handle
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 8

JamesMaeding
Advisor
Advisor

I use handles everywhere, so your app should work fine once coded.

Your idea of acad code only in one place sounds good, but I would interpret that as you simply want it minimized.

 

I would recommend you focus on the idea of minimizing transactions.

If you do that, you will go in the right direction, as you will then be writing functions to just do the specific task needed.

So if you have a task to get an object by its handle, that would be one helper function, and would be like:

public entity GetItemByHandle(transaction tr, string handle..){...}

 

if you feed in the transaction, you not only get rid of all the "lead up" code, you also achieve maximum speed.

 


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes
Message 7 of 8

BKSpurgeon
Collaborator
Collaborator

In addition to my estimable colleagues answers perhaps have a look around here: 

 

https://github.com/Hpadgroup/AcExtensionLibrary/tree/8f0984347414b440303a428284fe6c138251a616/src/Ac...

 

 

i myself am learning a lot from reading such code and Kean Walmsley's blog http://through-the-interface.typepad.com/through_the_interface/about-the-author.html

 

also well worth searching for tony tanzillo's past posts as well as many contributors to this forum and swamp forum.

 

i hope this helps.

Message 8 of 8

Anonymous
Not applicable

thanks to everyone for your very helpful insights and tips, i'm learning a bunch here!! Also found this amazing resource (https://wtertinek.com/) by searching Linq and acad based on _giles mention of linq (about which i knew nothing till now)...thank you all so much!!

0 Likes